1b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org/*
2b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *
4b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  Use of this source code is governed by a BSD-style license
5b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  that can be found in the LICENSE file in the root of the source
6b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  tree. An additional intellectual property rights grant can be found
7b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  in the file PATENTS.  All contributing project authors may
8b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  be found in the AUTHORS file in the root of the source tree.
9b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org */
10b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
11b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org#include "webrtc/modules/desktop_capture/window_capturer.h"
12b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
1312dc1a38ca54a000e4fecfbc6d41138b895c9ca5pbos@webrtc.org#include <assert.h>
14eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org#include <ApplicationServices/ApplicationServices.h>
15886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org#include <Cocoa/Cocoa.h>
16eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org#include <CoreFoundation/CoreFoundation.h>
17b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
1812b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org#include "webrtc/base/macutils.h"
1926b08605e2b99136fcc1cab0800234f469d6e236Peter Boström#include "webrtc/base/scoped_ref_ptr.h"
2012b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
21b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
2212b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
2312b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
2412b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org#include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
2598f53510b222f71fdd8b799b2f33737ceeb28c61Henrik Kjellander#include "webrtc/system_wrappers/include/logging.h"
2698f53510b222f71fdd8b799b2f33737ceeb28c61Henrik Kjellander#include "webrtc/system_wrappers/include/tick_util.h"
27b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
28b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgnamespace webrtc {
29b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
30b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgnamespace {
31b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
3212b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org// Returns true if the window exists.
33cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.orgbool IsWindowValid(CGWindowID id) {
34cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  CFArrayRef window_id_array =
35cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org      CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
36cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  CFArrayRef window_array =
37cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org      CGWindowListCreateDescriptionFromArray(window_id_array);
38cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  bool valid = window_array && CFArrayGetCount(window_array);
39cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  CFRelease(window_id_array);
40cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  CFRelease(window_array);
41cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org
42cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  return valid;
43cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org}
44cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org
45b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgclass WindowCapturerMac : public WindowCapturer {
46b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org public:
4726b08605e2b99136fcc1cab0800234f469d6e236Peter Boström  explicit WindowCapturerMac(rtc::scoped_refptr<FullScreenChromeWindowDetector>
4826b08605e2b99136fcc1cab0800234f469d6e236Peter Boström                                 full_screen_chrome_window_detector);
49b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  virtual ~WindowCapturerMac();
50b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
51b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  // WindowCapturer interface.
5214665ff7d4024d07e58622f498b23fd980001871kjellander@webrtc.org  bool GetWindowList(WindowList* windows) override;
5314665ff7d4024d07e58622f498b23fd980001871kjellander@webrtc.org  bool SelectWindow(WindowId id) override;
5414665ff7d4024d07e58622f498b23fd980001871kjellander@webrtc.org  bool BringSelectedWindowToFront() override;
55b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
56b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  // DesktopCapturer interface.
5714665ff7d4024d07e58622f498b23fd980001871kjellander@webrtc.org  void Start(Callback* callback) override;
5814665ff7d4024d07e58622f498b23fd980001871kjellander@webrtc.org  void Capture(const DesktopRegion& region) override;
59b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
60b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org private:
61b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  Callback* callback_;
6212b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org
6312b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org  // The window being captured.
64eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGWindowID window_id_;
65b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
6626b08605e2b99136fcc1cab0800234f469d6e236Peter Boström  rtc::scoped_refptr<FullScreenChromeWindowDetector>
6712b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org      full_screen_chrome_window_detector_;
6812b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org
693c089d751ede283e21e186885eaf705c3257ccd2henrikg  RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
70b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org};
71b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
7226b08605e2b99136fcc1cab0800234f469d6e236Peter BoströmWindowCapturerMac::WindowCapturerMac(rtc::scoped_refptr<
7326b08605e2b99136fcc1cab0800234f469d6e236Peter Boström    FullScreenChromeWindowDetector> full_screen_chrome_window_detector)
74eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    : callback_(NULL),
7512b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org      window_id_(0),
7612b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org      full_screen_chrome_window_detector_(full_screen_chrome_window_detector) {
77b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
78b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
79b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgWindowCapturerMac::~WindowCapturerMac() {
80b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
81b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
82b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgbool WindowCapturerMac::GetWindowList(WindowList* windows) {
83eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // Only get on screen, non-desktop windows.
84eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFArrayRef window_array = CGWindowListCopyWindowInfo(
85eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
86eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      kCGNullWindowID);
87eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (!window_array)
88eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return false;
89eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
90eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // Check windows to make sure they have an id, title, and use window layer
91eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // other than 0.
92eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFIndex count = CFArrayGetCount(window_array);
93eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  for (CFIndex i = 0; i < count; ++i) {
94eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
95eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFArrayGetValueAtIndex(window_array, i));
96eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFStringRef window_title = reinterpret_cast<CFStringRef>(
97eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFDictionaryGetValue(window, kCGWindowName));
98eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
99eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFDictionaryGetValue(window, kCGWindowNumber));
100eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
101eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFDictionaryGetValue(window, kCGWindowLayer));
102eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    if (window_title && window_id && window_layer) {
103eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      // Skip windows with layer=0 (menu, dock).
104eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      int layer;
105eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
106eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      if (layer != 0)
107eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        continue;
108eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
109eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      int id;
110eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CFNumberGetValue(window_id, kCFNumberIntType, &id);
111eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      WindowCapturer::Window window;
112eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      window.id = id;
11312b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org      if (!rtc::ToUtf8(window_title, &(window.title)) ||
114eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org          window.title.empty()) {
115eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        continue;
116eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      }
117eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      windows->push_back(window);
118eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    }
119eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
120eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
121eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFRelease(window_array);
122eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  return true;
123b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
124b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
125b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgbool WindowCapturerMac::SelectWindow(WindowId id) {
126cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  if (!IsWindowValid(id))
127eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return false;
128eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  window_id_ = id;
129eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  return true;
130b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
131b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
132886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.orgbool WindowCapturerMac::BringSelectedWindowToFront() {
133886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  if (!window_id_)
134886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org    return false;
135886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
136886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CGWindowID ids[1];
137886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  ids[0] = window_id_;
138886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFArrayRef window_id_array =
139886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org      CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
140886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
141886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFArrayRef window_array =
142886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org      CGWindowListCreateDescriptionFromArray(window_id_array);
143886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  if (window_array == NULL || 0 == CFArrayGetCount(window_array)) {
144886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org    // Could not find the window. It might have been closed.
145886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org    LOG(LS_INFO) << "Window not found";
146886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org    CFRelease(window_id_array);
147886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org    return false;
148886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  }
149886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
150886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
151886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org      CFArrayGetValueAtIndex(window_array, 0));
152886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
153886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org      CFDictionaryGetValue(window, kCGWindowOwnerPID));
154886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
155886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  int pid;
156886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
157886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
158886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  // TODO(jiayl): this will bring the process main window to the front. We
159886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  // should find a way to bring only the window to the front.
160886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  bool result =
161886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org      [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
162886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org          activateWithOptions: NSApplicationActivateIgnoringOtherApps];
163886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
164886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFRelease(window_id_array);
165886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  CFRelease(window_array);
166886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org  return result;
167886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org}
168886c94f07cf0a841b81cf9e69783bb1052d9c8a9jiayl@webrtc.org
169b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgvoid WindowCapturerMac::Start(Callback* callback) {
170b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  assert(!callback_);
171b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  assert(callback);
172b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
173b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  callback_ = callback;
174b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
175b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
176b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgvoid WindowCapturerMac::Capture(const DesktopRegion& region) {
177cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  if (!IsWindowValid(window_id_)) {
178cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org    callback_->OnCaptureCompleted(NULL);
179cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org    return;
180cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org  }
181cc1ba15fe737bfc58ef279d50d7e713cbd8b9310jiayl@webrtc.org
18212b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org  CGWindowID on_screen_window = window_id_;
18312b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org  if (full_screen_chrome_window_detector_) {
18412b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org    CGWindowID full_screen_window =
18512b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org        full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_);
18612b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org
18712b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org    if (full_screen_window != kCGNullWindowID)
18812b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org      on_screen_window = full_screen_window;
18912b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org  }
19012b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org
191eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGImageRef window_image = CGWindowListCreateImage(
192eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CGRectNull, kCGWindowListOptionIncludingWindow,
19312b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org      on_screen_window, kCGWindowImageBoundsIgnoreFraming);
194eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
195eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (!window_image) {
196eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    callback_->OnCaptureCompleted(NULL);
197eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return;
198eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
199eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
200eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
201eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (bits_per_pixel != 32) {
202eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
203eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFRelease(window_image);
204eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    callback_->OnCaptureCompleted(NULL);
205eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return;
206eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
207eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
208eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int width = CGImageGetWidth(window_image);
209eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int height = CGImageGetHeight(window_image);
210eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGDataProviderRef provider = CGImageGetDataProvider(window_image);
2112df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  CFDataRef cf_data = CGDataProviderCopyData(provider);
2122df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  DesktopFrame* frame = new BasicDesktopFrame(
2132df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org      DesktopSize(width, height));
2142df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org
2152df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  int src_stride = CGImageGetBytesPerRow(window_image);
2162df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  const uint8_t* src_data = CFDataGetBytePtr(cf_data);
2172df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  for (int y = 0; y < height; ++y) {
2182df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org    memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
2192df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org           DesktopFrame::kBytesPerPixel * width);
2202df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  }
2212df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org
2222df89c0c8b6416c69d70da5f29615733986a0716sergeyu@chromium.org  CFRelease(cf_data);
223eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFRelease(window_image);
224eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
225d402875fa50164c6533ef0f7b2098f73eb94b8a8sergeyu@chromium.org  frame->mutable_updated_region()->SetRect(
226d402875fa50164c6533ef0f7b2098f73eb94b8a8sergeyu@chromium.org      DesktopRect::MakeSize(frame->size()));
227d402875fa50164c6533ef0f7b2098f73eb94b8a8sergeyu@chromium.org
228eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  callback_->OnCaptureCompleted(frame);
22912b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org
23012b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org  if (full_screen_chrome_window_detector_)
23112b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org    full_screen_chrome_window_detector_->UpdateWindowListIfNeeded(window_id_);
232b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
233b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
234b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}  // namespace
235b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
236b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org// static
237894e6fe9ea16a63537ec6d453c81566d02f66059sergeyu@chromium.orgWindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
23812b4efefddc0ce727f228fd1a5024e70b4687ac4jiayl@webrtc.org  return new WindowCapturerMac(options.full_screen_chrome_window_detector());
239b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
240b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
241b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}  // namespace webrtc
242