/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include #include "api/scoped_refptr.h" #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/mac/desktop_configuration.h" #include "modules/desktop_capture/mac/desktop_configuration_monitor.h" #include "modules/desktop_capture/mac/desktop_frame_cgimage.h" #include "modules/desktop_capture/mac/window_list_utils.h" #include "modules/desktop_capture/window_finder_mac.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/trace_event.h" namespace webrtc { namespace { // Returns true if the window exists. bool IsWindowValid(CGWindowID id) { CFArrayRef window_id_array = CFArrayCreate(nullptr, reinterpret_cast(&id), 1, nullptr); CFArrayRef window_array = CGWindowListCreateDescriptionFromArray(window_id_array); bool valid = window_array && CFArrayGetCount(window_array); CFRelease(window_id_array); CFRelease(window_array); return valid; } class WindowCapturerMac : public DesktopCapturer { public: explicit WindowCapturerMac( rtc::scoped_refptr full_screen_window_detector, rtc::scoped_refptr configuration_monitor); ~WindowCapturerMac() override; WindowCapturerMac(const WindowCapturerMac&) = delete; WindowCapturerMac& operator=(const WindowCapturerMac&) = delete; // DesktopCapturer interface. void Start(Callback* callback) override; void CaptureFrame() override; bool GetSourceList(SourceList* sources) override; bool SelectSource(SourceId id) override; bool FocusOnSelectedSource() override; bool IsOccluded(const DesktopVector& pos) override; private: Callback* callback_ = nullptr; // The window being captured. CGWindowID window_id_ = 0; rtc::scoped_refptr full_screen_window_detector_; const rtc::scoped_refptr configuration_monitor_; WindowFinderMac window_finder_; // Used to make sure that we only log the usage of fullscreen detection once. bool fullscreen_usage_logged_ = false; }; WindowCapturerMac::WindowCapturerMac( rtc::scoped_refptr full_screen_window_detector, rtc::scoped_refptr configuration_monitor) : full_screen_window_detector_(std::move(full_screen_window_detector)), configuration_monitor_(std::move(configuration_monitor)), window_finder_(configuration_monitor_) {} WindowCapturerMac::~WindowCapturerMac() {} bool WindowCapturerMac::GetSourceList(SourceList* sources) { return webrtc::GetWindowList(sources, true, true); } bool WindowCapturerMac::SelectSource(SourceId id) { if (!IsWindowValid(id)) return false; window_id_ = id; return true; } bool WindowCapturerMac::FocusOnSelectedSource() { if (!window_id_) return false; CGWindowID ids[1]; ids[0] = window_id_; CFArrayRef window_id_array = CFArrayCreate(nullptr, reinterpret_cast(&ids), 1, nullptr); CFArrayRef window_array = CGWindowListCreateDescriptionFromArray(window_id_array); if (!window_array || 0 == CFArrayGetCount(window_array)) { // Could not find the window. It might have been closed. RTC_LOG(LS_INFO) << "Window not found"; CFRelease(window_id_array); return false; } CFDictionaryRef window = reinterpret_cast( CFArrayGetValueAtIndex(window_array, 0)); CFNumberRef pid_ref = reinterpret_cast( CFDictionaryGetValue(window, kCGWindowOwnerPID)); int pid; CFNumberGetValue(pid_ref, kCFNumberIntType, &pid); // TODO(jiayl): this will bring the process main window to the front. We // should find a way to bring only the window to the front. bool result = [[NSRunningApplication runningApplicationWithProcessIdentifier: pid] activateWithOptions: NSApplicationActivateIgnoringOtherApps]; CFRelease(window_id_array); CFRelease(window_array); return result; } bool WindowCapturerMac::IsOccluded(const DesktopVector& pos) { DesktopVector sys_pos = pos; if (configuration_monitor_) { auto configuration = configuration_monitor_->desktop_configuration(); sys_pos = pos.add(configuration.bounds.top_left()); } return window_finder_.GetWindowUnderPoint(sys_pos) != window_id_; } void WindowCapturerMac::Start(Callback* callback) { RTC_DCHECK(!callback_); RTC_DCHECK(callback); callback_ = callback; } void WindowCapturerMac::CaptureFrame() { TRACE_EVENT0("webrtc", "WindowCapturerMac::CaptureFrame"); if (!IsWindowValid(window_id_)) { RTC_LOG(LS_ERROR) << "The window is not valid any longer."; callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); return; } CGWindowID on_screen_window = window_id_; if (full_screen_window_detector_) { full_screen_window_detector_->UpdateWindowListIfNeeded( window_id_, [](DesktopCapturer::SourceList* sources) { // Not using webrtc::GetWindowList(sources, true, false) // as it doesn't allow to have in the result window with // empty title along with titled window owned by the same pid. return webrtc::GetWindowList( [sources](CFDictionaryRef window) { WindowId window_id = GetWindowId(window); int pid = GetWindowOwnerPid(window); if (window_id != kNullWindowId) { sources->push_back(DesktopCapturer::Source{window_id, pid, GetWindowTitle(window)}); } return true; }, true, false); }); CGWindowID full_screen_window = full_screen_window_detector_->FindFullScreenWindow(window_id_); if (full_screen_window != kCGNullWindowID) { // If this is the first time this happens, report to UMA that the feature is active. if (!fullscreen_usage_logged_) { LogDesktopCapturerFullscreenDetectorUsage(); fullscreen_usage_logged_ = true; } on_screen_window = full_screen_window; } } std::unique_ptr frame = DesktopFrameCGImage::CreateForWindow(on_screen_window); if (!frame) { RTC_LOG(LS_WARNING) << "Temporarily failed to capture window."; callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); return; } frame->mutable_updated_region()->SetRect( DesktopRect::MakeSize(frame->size())); frame->set_top_left(GetWindowBounds(on_screen_window).top_left()); float scale_factor = GetWindowScaleFactor(window_id_, frame->size()); frame->set_dpi(DesktopVector(kStandardDPI * scale_factor, kStandardDPI * scale_factor)); callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); } } // namespace // static std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( const DesktopCaptureOptions& options) { return std::unique_ptr(new WindowCapturerMac( options.full_screen_window_detector(), options.configuration_monitor())); } } // namespace webrtc