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