• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <assert.h>
12#include <ApplicationServices/ApplicationServices.h>
13#include <Cocoa/Cocoa.h>
14#include <CoreFoundation/CoreFoundation.h>
15
16#include <utility>
17
18#include "api/scoped_refptr.h"
19#include "modules/desktop_capture/desktop_capture_options.h"
20#include "modules/desktop_capture/desktop_capturer.h"
21#include "modules/desktop_capture/desktop_frame.h"
22#include "modules/desktop_capture/mac/desktop_configuration.h"
23#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
24#include "modules/desktop_capture/mac/desktop_frame_cgimage.h"
25#include "modules/desktop_capture/mac/window_list_utils.h"
26#include "modules/desktop_capture/window_finder_mac.h"
27#include "rtc_base/constructor_magic.h"
28#include "rtc_base/logging.h"
29#include "rtc_base/trace_event.h"
30
31namespace webrtc {
32
33namespace {
34
35// Returns true if the window exists.
36bool IsWindowValid(CGWindowID id) {
37  CFArrayRef window_id_array =
38      CFArrayCreate(nullptr, reinterpret_cast<const void**>(&id), 1, nullptr);
39  CFArrayRef window_array =
40      CGWindowListCreateDescriptionFromArray(window_id_array);
41  bool valid = window_array && CFArrayGetCount(window_array);
42  CFRelease(window_id_array);
43  CFRelease(window_array);
44
45  return valid;
46}
47
48class WindowCapturerMac : public DesktopCapturer {
49 public:
50  explicit WindowCapturerMac(
51      rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
52      rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor);
53  ~WindowCapturerMac() override;
54
55  // DesktopCapturer interface.
56  void Start(Callback* callback) override;
57  void CaptureFrame() override;
58  bool GetSourceList(SourceList* sources) override;
59  bool SelectSource(SourceId id) override;
60  bool FocusOnSelectedSource() override;
61  bool IsOccluded(const DesktopVector& pos) override;
62
63 private:
64  Callback* callback_ = nullptr;
65
66  // The window being captured.
67  CGWindowID window_id_ = 0;
68
69  rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
70
71  const rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
72
73  WindowFinderMac window_finder_;
74
75  RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
76};
77
78WindowCapturerMac::WindowCapturerMac(
79    rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
80    rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor)
81    : full_screen_window_detector_(std::move(full_screen_window_detector)),
82      configuration_monitor_(std::move(configuration_monitor)),
83      window_finder_(configuration_monitor_) {}
84
85WindowCapturerMac::~WindowCapturerMac() {}
86
87bool WindowCapturerMac::GetSourceList(SourceList* sources) {
88  return webrtc::GetWindowList(sources, true, true);
89}
90
91bool WindowCapturerMac::SelectSource(SourceId id) {
92  if (!IsWindowValid(id))
93    return false;
94  window_id_ = id;
95  return true;
96}
97
98bool WindowCapturerMac::FocusOnSelectedSource() {
99  if (!window_id_)
100    return false;
101
102  CGWindowID ids[1];
103  ids[0] = window_id_;
104  CFArrayRef window_id_array =
105      CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
106
107  CFArrayRef window_array =
108      CGWindowListCreateDescriptionFromArray(window_id_array);
109  if (!window_array || 0 == CFArrayGetCount(window_array)) {
110    // Could not find the window. It might have been closed.
111    RTC_LOG(LS_INFO) << "Window not found";
112    CFRelease(window_id_array);
113    return false;
114  }
115
116  CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
117      CFArrayGetValueAtIndex(window_array, 0));
118  CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
119      CFDictionaryGetValue(window, kCGWindowOwnerPID));
120
121  int pid;
122  CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
123
124  // TODO(jiayl): this will bring the process main window to the front. We
125  // should find a way to bring only the window to the front.
126  bool result =
127      [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
128          activateWithOptions: NSApplicationActivateIgnoringOtherApps];
129
130  CFRelease(window_id_array);
131  CFRelease(window_array);
132  return result;
133}
134
135bool WindowCapturerMac::IsOccluded(const DesktopVector& pos) {
136  DesktopVector sys_pos = pos;
137  if (configuration_monitor_) {
138    auto configuration = configuration_monitor_->desktop_configuration();
139    sys_pos = pos.add(configuration.bounds.top_left());
140  }
141  return window_finder_.GetWindowUnderPoint(sys_pos) != window_id_;
142}
143
144void WindowCapturerMac::Start(Callback* callback) {
145  assert(!callback_);
146  assert(callback);
147
148  callback_ = callback;
149}
150
151void WindowCapturerMac::CaptureFrame() {
152  TRACE_EVENT0("webrtc", "WindowCapturerMac::CaptureFrame");
153
154  if (!IsWindowValid(window_id_)) {
155    RTC_LOG(LS_ERROR) << "The window is not valid any longer.";
156    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
157    return;
158  }
159
160  CGWindowID on_screen_window = window_id_;
161  if (full_screen_window_detector_) {
162    full_screen_window_detector_->UpdateWindowListIfNeeded(
163        window_id_, [](DesktopCapturer::SourceList* sources) {
164          return webrtc::GetWindowList(sources, true, false);
165        });
166
167    CGWindowID full_screen_window = full_screen_window_detector_->FindFullScreenWindow(window_id_);
168
169    if (full_screen_window != kCGNullWindowID) on_screen_window = full_screen_window;
170  }
171
172  std::unique_ptr<DesktopFrame> frame = DesktopFrameCGImage::CreateForWindow(on_screen_window);
173  if (!frame) {
174    RTC_LOG(LS_WARNING) << "Temporarily failed to capture window.";
175    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
176    return;
177  }
178
179  frame->mutable_updated_region()->SetRect(
180      DesktopRect::MakeSize(frame->size()));
181  frame->set_top_left(GetWindowBounds(on_screen_window).top_left());
182
183  float scale_factor = GetWindowScaleFactor(window_id_, frame->size());
184  frame->set_dpi(DesktopVector(kStandardDPI * scale_factor, kStandardDPI * scale_factor));
185
186  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
187}
188
189}  // namespace
190
191// static
192std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
193    const DesktopCaptureOptions& options) {
194  return std::unique_ptr<DesktopCapturer>(new WindowCapturerMac(
195      options.full_screen_window_detector(), options.configuration_monitor()));
196}
197
198}  // namespace webrtc
199