diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm b/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm new file mode 100644 index 0000000000..8f5dc25516 --- /dev/null +++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm @@ -0,0 +1,220 @@ +/* + * 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 <ApplicationServices/ApplicationServices.h> +#include <Cocoa/Cocoa.h> +#include <CoreFoundation/CoreFoundation.h> + +#include <utility> + +#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<const void**>(&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<FullScreenWindowDetector> full_screen_window_detector, + rtc::scoped_refptr<DesktopConfigurationMonitor> 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<FullScreenWindowDetector> full_screen_window_detector_; + + const rtc::scoped_refptr<DesktopConfigurationMonitor> 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<FullScreenWindowDetector> full_screen_window_detector, + rtc::scoped_refptr<DesktopConfigurationMonitor> 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<const void**>(&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<CFDictionaryRef>( + CFArrayGetValueAtIndex(window_array, 0)); + CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>( + 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:0]; + + 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<DesktopFrame> 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> DesktopCapturer::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { + return std::unique_ptr<DesktopCapturer>(new WindowCapturerMac( + options.full_screen_window_detector(), options.configuration_monitor())); +} + +} // namespace webrtc |