summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/desktop_capture/mac
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/desktop_capture/mac')
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h96
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm189
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc73
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h55
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h58
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm108
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm61
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm70
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc238
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h24
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h128
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm633
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc430
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h117
16 files changed, 2384 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h
new file mode 100644
index 0000000000..2ad5474e44
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Describes the configuration of a specific display.
+struct MacDisplayConfiguration {
+ MacDisplayConfiguration();
+ MacDisplayConfiguration(const MacDisplayConfiguration& other);
+ MacDisplayConfiguration(MacDisplayConfiguration&& other);
+ ~MacDisplayConfiguration();
+
+ MacDisplayConfiguration& operator=(const MacDisplayConfiguration& other);
+ MacDisplayConfiguration& operator=(MacDisplayConfiguration&& other);
+
+ // Cocoa identifier for this display.
+ CGDirectDisplayID id = 0;
+
+ // Bounds of this display in Density-Independent Pixels (DIPs).
+ DesktopRect bounds;
+
+ // Bounds of this display in physical pixels.
+ DesktopRect pixel_bounds;
+
+ // Scale factor from DIPs to physical pixels.
+ float dip_to_pixel_scale = 1.0f;
+
+ // Display type, built-in or external.
+ bool is_builtin;
+};
+
+typedef std::vector<MacDisplayConfiguration> MacDisplayConfigurations;
+
+// Describes the configuration of the whole desktop.
+struct RTC_EXPORT MacDesktopConfiguration {
+ // Used to request bottom-up or top-down coordinates.
+ enum Origin { BottomLeftOrigin, TopLeftOrigin };
+
+ MacDesktopConfiguration();
+ MacDesktopConfiguration(const MacDesktopConfiguration& other);
+ MacDesktopConfiguration(MacDesktopConfiguration&& other);
+ ~MacDesktopConfiguration();
+
+ MacDesktopConfiguration& operator=(const MacDesktopConfiguration& other);
+ MacDesktopConfiguration& operator=(MacDesktopConfiguration&& other);
+
+ // Returns the desktop & display configurations.
+ // If BottomLeftOrigin is used, the output is in Cocoa-style "bottom-up"
+ // (the origin is the bottom-left of the primary monitor, and coordinates
+ // increase as you move up the screen). Otherwise, the configuration will be
+ // converted to follow top-left coordinate system as Windows and X11.
+ static MacDesktopConfiguration GetCurrent(Origin origin);
+
+ // Returns true if the given desktop configuration equals this one.
+ bool Equals(const MacDesktopConfiguration& other);
+
+ // If `id` corresponds to the built-in display, return its configuration,
+ // otherwise return the configuration for the display with the specified id,
+ // or nullptr if no such display exists.
+ const MacDisplayConfiguration* FindDisplayConfigurationById(
+ CGDirectDisplayID id);
+
+ // Bounds of the desktop excluding monitors with DPI settings different from
+ // the main monitor. In Density-Independent Pixels (DIPs).
+ DesktopRect bounds;
+
+ // Same as bounds, but expressed in physical pixels.
+ DesktopRect pixel_bounds;
+
+ // Scale factor from DIPs to physical pixels.
+ float dip_to_pixel_scale = 1.0f;
+
+ // Configurations of the displays making up the desktop area.
+ MacDisplayConfigurations displays;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm
new file mode 100644
index 0000000000..93fb3f6226
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm
@@ -0,0 +1,189 @@
+/*
+ * 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 "modules/desktop_capture/mac/desktop_configuration.h"
+
+#include <math.h>
+#include <algorithm>
+#include <Cocoa/Cocoa.h>
+
+#include "rtc_base/checks.h"
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+
+@interface NSScreen (LionAPI)
+- (CGFloat)backingScaleFactor;
+- (NSRect)convertRectToBacking:(NSRect)aRect;
+@end
+
+#endif // MAC_OS_X_VERSION_10_7
+
+namespace webrtc {
+
+namespace {
+
+DesktopRect NSRectToDesktopRect(const NSRect& ns_rect) {
+ return DesktopRect::MakeLTRB(
+ static_cast<int>(floor(ns_rect.origin.x)),
+ static_cast<int>(floor(ns_rect.origin.y)),
+ static_cast<int>(ceil(ns_rect.origin.x + ns_rect.size.width)),
+ static_cast<int>(ceil(ns_rect.origin.y + ns_rect.size.height)));
+}
+
+// Inverts the position of `rect` from bottom-up coordinates to top-down,
+// relative to `bounds`.
+void InvertRectYOrigin(const DesktopRect& bounds,
+ DesktopRect* rect) {
+ RTC_DCHECK_EQ(bounds.top(), 0);
+ *rect = DesktopRect::MakeXYWH(
+ rect->left(), bounds.bottom() - rect->bottom(),
+ rect->width(), rect->height());
+}
+
+MacDisplayConfiguration GetConfigurationForScreen(NSScreen* screen) {
+ MacDisplayConfiguration display_config;
+
+ // Fetch the NSScreenNumber, which is also the CGDirectDisplayID.
+ NSDictionary* device_description = [screen deviceDescription];
+ display_config.id = static_cast<CGDirectDisplayID>(
+ [[device_description objectForKey:@"NSScreenNumber"] intValue]);
+
+ // Determine the display's logical & physical dimensions.
+ NSRect ns_bounds = [screen frame];
+ display_config.bounds = NSRectToDesktopRect(ns_bounds);
+
+ // If the host is running Mac OS X 10.7+ or later, query the scaling factor
+ // between logical and physical (aka "backing") pixels, otherwise assume 1:1.
+ if ([screen respondsToSelector:@selector(backingScaleFactor)] &&
+ [screen respondsToSelector:@selector(convertRectToBacking:)]) {
+ display_config.dip_to_pixel_scale = [screen backingScaleFactor];
+ NSRect ns_pixel_bounds = [screen convertRectToBacking: ns_bounds];
+ display_config.pixel_bounds = NSRectToDesktopRect(ns_pixel_bounds);
+ } else {
+ display_config.pixel_bounds = display_config.bounds;
+ }
+
+ // Determine if the display is built-in or external.
+ display_config.is_builtin = CGDisplayIsBuiltin(display_config.id);
+
+ return display_config;
+}
+
+} // namespace
+
+MacDisplayConfiguration::MacDisplayConfiguration() = default;
+MacDisplayConfiguration::MacDisplayConfiguration(
+ const MacDisplayConfiguration& other) = default;
+MacDisplayConfiguration::MacDisplayConfiguration(
+ MacDisplayConfiguration&& other) = default;
+MacDisplayConfiguration::~MacDisplayConfiguration() = default;
+
+MacDisplayConfiguration& MacDisplayConfiguration::operator=(
+ const MacDisplayConfiguration& other) = default;
+MacDisplayConfiguration& MacDisplayConfiguration::operator=(
+ MacDisplayConfiguration&& other) = default;
+
+MacDesktopConfiguration::MacDesktopConfiguration() = default;
+MacDesktopConfiguration::MacDesktopConfiguration(
+ const MacDesktopConfiguration& other) = default;
+MacDesktopConfiguration::MacDesktopConfiguration(
+ MacDesktopConfiguration&& other) = default;
+MacDesktopConfiguration::~MacDesktopConfiguration() = default;
+
+MacDesktopConfiguration& MacDesktopConfiguration::operator=(
+ const MacDesktopConfiguration& other) = default;
+MacDesktopConfiguration& MacDesktopConfiguration::operator=(
+ MacDesktopConfiguration&& other) = default;
+
+// static
+MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
+ MacDesktopConfiguration desktop_config;
+
+ NSArray* screens = [NSScreen screens];
+ RTC_DCHECK(screens);
+
+ // Iterator over the monitors, adding the primary monitor and monitors whose
+ // DPI match that of the primary monitor.
+ for (NSUInteger i = 0; i < [screens count]; ++i) {
+ MacDisplayConfiguration display_config =
+ GetConfigurationForScreen([screens objectAtIndex: i]);
+
+ if (i == 0)
+ desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale;
+
+ // Cocoa uses bottom-up coordinates, so if the caller wants top-down then
+ // we need to invert the positions of secondary monitors relative to the
+ // primary one (the primary monitor's position is (0,0) in both systems).
+ if (i > 0 && origin == TopLeftOrigin) {
+ InvertRectYOrigin(desktop_config.displays[0].bounds,
+ &display_config.bounds);
+ // `display_bounds` is density dependent, so we need to convert the
+ // primay monitor's position into the secondary monitor's density context.
+ float scaling_factor = display_config.dip_to_pixel_scale /
+ desktop_config.displays[0].dip_to_pixel_scale;
+ DesktopRect primary_bounds = DesktopRect::MakeLTRB(
+ desktop_config.displays[0].pixel_bounds.left() * scaling_factor,
+ desktop_config.displays[0].pixel_bounds.top() * scaling_factor,
+ desktop_config.displays[0].pixel_bounds.right() * scaling_factor,
+ desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor);
+ InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds);
+ }
+
+ // Add the display to the configuration.
+ desktop_config.displays.push_back(display_config);
+
+ // Update the desktop bounds to account for this display, unless the current
+ // display uses different DPI settings.
+ if (display_config.dip_to_pixel_scale ==
+ desktop_config.dip_to_pixel_scale) {
+ desktop_config.bounds.UnionWith(display_config.bounds);
+ desktop_config.pixel_bounds.UnionWith(display_config.pixel_bounds);
+ }
+ }
+
+ return desktop_config;
+}
+
+// For convenience of comparing MacDisplayConfigurations in
+// MacDesktopConfiguration::Equals.
+bool operator==(const MacDisplayConfiguration& left,
+ const MacDisplayConfiguration& right) {
+ return left.id == right.id &&
+ left.bounds.equals(right.bounds) &&
+ left.pixel_bounds.equals(right.pixel_bounds) &&
+ left.dip_to_pixel_scale == right.dip_to_pixel_scale;
+}
+
+bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
+ return bounds.equals(other.bounds) &&
+ pixel_bounds.equals(other.pixel_bounds) &&
+ dip_to_pixel_scale == other.dip_to_pixel_scale &&
+ displays == other.displays;
+}
+
+const MacDisplayConfiguration*
+MacDesktopConfiguration::FindDisplayConfigurationById(
+ CGDirectDisplayID id) {
+ bool is_builtin = CGDisplayIsBuiltin(id);
+ for (MacDisplayConfigurations::const_iterator it = displays.begin();
+ it != displays.end(); ++it) {
+ // The MBP having both discrete and integrated graphic cards will do
+ // automate graphics switching by default. When it switches from discrete to
+ // integrated one, the current display ID of the built-in display will
+ // change and this will cause screen capture stops.
+ // So make screen capture of built-in display continuing even if its display
+ // ID is changed.
+ if ((is_builtin && it->is_builtin) || (!is_builtin && it->id == id)) return &(*it);
+ }
+ return NULL;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc
new file mode 100644
index 0000000000..048a679ecc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014 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 "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+DesktopConfigurationMonitor::DesktopConfigurationMonitor() {
+ CGError err = CGDisplayRegisterReconfigurationCallback(
+ DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
+ if (err != kCGErrorSuccess)
+ RTC_LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
+ MutexLock lock(&desktop_configuration_lock_);
+ desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+}
+
+DesktopConfigurationMonitor::~DesktopConfigurationMonitor() {
+ CGError err = CGDisplayRemoveReconfigurationCallback(
+ DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
+ if (err != kCGErrorSuccess)
+ RTC_LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
+}
+
+MacDesktopConfiguration DesktopConfigurationMonitor::desktop_configuration() {
+ MutexLock lock(&desktop_configuration_lock_);
+ return desktop_configuration_;
+}
+
+// static
+// This method may be called on any system thread.
+void DesktopConfigurationMonitor::DisplaysReconfiguredCallback(
+ CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void* user_parameter) {
+ DesktopConfigurationMonitor* monitor =
+ reinterpret_cast<DesktopConfigurationMonitor*>(user_parameter);
+ monitor->DisplaysReconfigured(display, flags);
+}
+
+void DesktopConfigurationMonitor::DisplaysReconfigured(
+ CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags) {
+ TRACE_EVENT0("webrtc", "DesktopConfigurationMonitor::DisplaysReconfigured");
+ RTC_LOG(LS_INFO) << "DisplaysReconfigured: "
+ "DisplayID "
+ << display << "; ChangeSummaryFlags " << flags;
+
+ if (flags & kCGDisplayBeginConfigurationFlag) {
+ reconfiguring_displays_.insert(display);
+ return;
+ }
+
+ reconfiguring_displays_.erase(display);
+ if (reconfiguring_displays_.empty()) {
+ MutexLock lock(&desktop_configuration_lock_);
+ desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h
new file mode 100644
index 0000000000..747295a538
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <memory>
+#include <set>
+
+#include "api/ref_counted_base.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "rtc_base/synchronization/mutex.h"
+
+namespace webrtc {
+
+// The class provides functions to synchronize capturing and display
+// reconfiguring across threads, and the up-to-date MacDesktopConfiguration.
+class DesktopConfigurationMonitor final
+ : public rtc::RefCountedNonVirtual<DesktopConfigurationMonitor> {
+ public:
+ DesktopConfigurationMonitor();
+ ~DesktopConfigurationMonitor();
+
+ DesktopConfigurationMonitor(const DesktopConfigurationMonitor&) = delete;
+ DesktopConfigurationMonitor& operator=(const DesktopConfigurationMonitor&) =
+ delete;
+
+ // Returns the current desktop configuration.
+ MacDesktopConfiguration desktop_configuration();
+
+ private:
+ static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void* user_parameter);
+ void DisplaysReconfigured(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags);
+
+ Mutex desktop_configuration_lock_;
+ MacDesktopConfiguration desktop_configuration_
+ RTC_GUARDED_BY(&desktop_configuration_lock_);
+ std::set<CGDirectDisplayID> reconfiguring_displays_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h
new file mode 100644
index 0000000000..d6279f9b36
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_CGIMAGE_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_CGIMAGE_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+class DesktopFrameCGImage final : public DesktopFrame {
+ public:
+ // Create an image containing a snapshot of the display at the time this is
+ // being called.
+ static std::unique_ptr<DesktopFrameCGImage> CreateForDisplay(
+ CGDirectDisplayID display_id);
+
+ // Create an image containing a snaphot of the given window at the time this
+ // is being called. This also works when the window is overlapped or in
+ // another workspace.
+ static std::unique_ptr<DesktopFrameCGImage> CreateForWindow(
+ CGWindowID window_id);
+
+ ~DesktopFrameCGImage() override;
+
+ DesktopFrameCGImage(const DesktopFrameCGImage&) = delete;
+ DesktopFrameCGImage& operator=(const DesktopFrameCGImage&) = delete;
+
+ private:
+ static std::unique_ptr<DesktopFrameCGImage> CreateFromCGImage(
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image);
+
+ // This constructor expects `cg_image` to hold a non-null CGImageRef.
+ DesktopFrameCGImage(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image,
+ rtc::ScopedCFTypeRef<CFDataRef> cg_data);
+
+ const rtc::ScopedCFTypeRef<CGImageRef> cg_image_;
+ const rtc::ScopedCFTypeRef<CFDataRef> cg_data_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_CGIMAGE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm
new file mode 100644
index 0000000000..0fb69b272d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018 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 "modules/desktop_capture/mac/desktop_frame_cgimage.h"
+
+#include <AvailabilityMacros.h>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateForDisplay(
+ CGDirectDisplayID display_id) {
+ // Create an image containing a snapshot of the display.
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image(CGDisplayCreateImage(display_id));
+ if (!cg_image) {
+ return nullptr;
+ }
+
+ return DesktopFrameCGImage::CreateFromCGImage(cg_image);
+}
+
+// static
+std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateForWindow(CGWindowID window_id) {
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image(
+ CGWindowListCreateImage(CGRectNull,
+ kCGWindowListOptionIncludingWindow,
+ window_id,
+ kCGWindowImageBoundsIgnoreFraming));
+ if (!cg_image) {
+ return nullptr;
+ }
+
+ return DesktopFrameCGImage::CreateFromCGImage(cg_image);
+}
+
+// static
+std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateFromCGImage(
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image) {
+ // Verify that the image has 32-bit depth.
+ int bits_per_pixel = CGImageGetBitsPerPixel(cg_image.get());
+ if (bits_per_pixel / 8 != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG(LS_ERROR) << "CGDisplayCreateImage() returned imaged with " << bits_per_pixel
+ << " bits per pixel. Only 32-bit depth is supported.";
+ return nullptr;
+ }
+
+ // Request access to the raw pixel data via the image's DataProvider.
+ CGDataProviderRef cg_provider = CGImageGetDataProvider(cg_image.get());
+ RTC_DCHECK(cg_provider);
+
+ // CGDataProviderCopyData returns a new data object containing a copy of the provider’s
+ // data.
+ rtc::ScopedCFTypeRef<CFDataRef> cg_data(CGDataProviderCopyData(cg_provider));
+ RTC_DCHECK(cg_data);
+
+ // CFDataGetBytePtr returns a read-only pointer to the bytes of a CFData object.
+ uint8_t* data = const_cast<uint8_t*>(CFDataGetBytePtr(cg_data.get()));
+ RTC_DCHECK(data);
+
+ DesktopSize size(CGImageGetWidth(cg_image.get()), CGImageGetHeight(cg_image.get()));
+ int stride = CGImageGetBytesPerRow(cg_image.get());
+
+ std::unique_ptr<DesktopFrameCGImage> frame(
+ new DesktopFrameCGImage(size, stride, data, cg_image, cg_data));
+
+ CGColorSpaceRef cg_color_space = CGImageGetColorSpace(cg_image.get());
+ if (cg_color_space) {
+#if !defined(MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13
+ rtc::ScopedCFTypeRef<CFDataRef> cf_icc_profile(CGColorSpaceCopyICCProfile(cg_color_space));
+#else
+ rtc::ScopedCFTypeRef<CFDataRef> cf_icc_profile(CGColorSpaceCopyICCData(cg_color_space));
+#endif
+ if (cf_icc_profile) {
+ const uint8_t* data_as_byte =
+ reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(cf_icc_profile.get()));
+ const size_t data_size = CFDataGetLength(cf_icc_profile.get());
+ if (data_as_byte && data_size > 0) {
+ frame->set_icc_profile(std::vector<uint8_t>(data_as_byte, data_as_byte + data_size));
+ }
+ }
+ }
+
+ return frame;
+}
+
+DesktopFrameCGImage::DesktopFrameCGImage(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image,
+ rtc::ScopedCFTypeRef<CFDataRef> cg_data)
+ : DesktopFrame(size, stride, data, nullptr), cg_image_(cg_image), cg_data_(cg_data) {
+ RTC_DCHECK(cg_image_);
+ RTC_DCHECK(cg_data_);
+}
+
+DesktopFrameCGImage::~DesktopFrameCGImage() = default;
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h
new file mode 100644
index 0000000000..73da0f693c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_IOSURFACE_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_IOSURFACE_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <IOSurface/IOSurface.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+class DesktopFrameIOSurface final : public DesktopFrame {
+ public:
+ // Lock an IOSurfaceRef containing a snapshot of a display. Return NULL if
+ // failed to lock.
+ static std::unique_ptr<DesktopFrameIOSurface> Wrap(
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface);
+
+ ~DesktopFrameIOSurface() override;
+
+ DesktopFrameIOSurface(const DesktopFrameIOSurface&) = delete;
+ DesktopFrameIOSurface& operator=(const DesktopFrameIOSurface&) = delete;
+
+ private:
+ // This constructor expects `io_surface` to hold a non-null IOSurfaceRef.
+ explicit DesktopFrameIOSurface(rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface);
+
+ const rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_IOSURFACE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm
new file mode 100644
index 0000000000..b59b319db9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 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 "modules/desktop_capture/mac/desktop_frame_iosurface.h"
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopFrameIOSurface> DesktopFrameIOSurface::Wrap(
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface) {
+ if (!io_surface) {
+ return nullptr;
+ }
+
+ IOSurfaceIncrementUseCount(io_surface.get());
+ IOReturn status = IOSurfaceLock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
+ if (status != kIOReturnSuccess) {
+ RTC_LOG(LS_ERROR) << "Failed to lock the IOSurface with status " << status;
+ IOSurfaceDecrementUseCount(io_surface.get());
+ return nullptr;
+ }
+
+ // Verify that the image has 32-bit depth.
+ int bytes_per_pixel = IOSurfaceGetBytesPerElement(io_surface.get());
+ if (bytes_per_pixel != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG(LS_ERROR) << "CGDisplayStream handler returned IOSurface with " << (8 * bytes_per_pixel)
+ << " bits per pixel. Only 32-bit depth is supported.";
+ IOSurfaceUnlock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
+ IOSurfaceDecrementUseCount(io_surface.get());
+ return nullptr;
+ }
+
+ return std::unique_ptr<DesktopFrameIOSurface>(new DesktopFrameIOSurface(io_surface));
+}
+
+DesktopFrameIOSurface::DesktopFrameIOSurface(rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface)
+ : DesktopFrame(
+ DesktopSize(IOSurfaceGetWidth(io_surface.get()), IOSurfaceGetHeight(io_surface.get())),
+ IOSurfaceGetBytesPerRow(io_surface.get()),
+ static_cast<uint8_t*>(IOSurfaceGetBaseAddress(io_surface.get())),
+ nullptr),
+ io_surface_(io_surface) {
+ RTC_DCHECK(io_surface_);
+}
+
+DesktopFrameIOSurface::~DesktopFrameIOSurface() {
+ IOSurfaceUnlock(io_surface_.get(), kIOSurfaceLockReadOnly, nullptr);
+ IOSurfaceDecrementUseCount(io_surface_.get());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
new file mode 100644
index 0000000000..aad28d2f30
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_PROVIDER_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_PROVIDER_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <IOSurface/IOSurface.h>
+
+#include <map>
+#include <memory>
+
+#include "api/sequence_checker.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+class DesktopFrameProvider {
+ public:
+ explicit DesktopFrameProvider(bool allow_iosurface);
+ ~DesktopFrameProvider();
+
+ DesktopFrameProvider(const DesktopFrameProvider&) = delete;
+ DesktopFrameProvider& operator=(const DesktopFrameProvider&) = delete;
+
+ // The caller takes ownership of the returned desktop frame. Otherwise
+ // returns null if `display_id` is invalid or not ready. Note that this
+ // function does not remove the frame from the internal container. Caller
+ // has to call the Release function.
+ std::unique_ptr<DesktopFrame> TakeLatestFrameForDisplay(
+ CGDirectDisplayID display_id);
+
+ // OS sends the latest IOSurfaceRef through
+ // CGDisplayStreamFrameAvailableHandler callback; we store it here.
+ void InvalidateIOSurface(CGDirectDisplayID display_id,
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface);
+
+ // Expected to be called before stopping the CGDisplayStreamRef streams.
+ void Release();
+
+ private:
+ SequenceChecker thread_checker_;
+ const bool allow_iosurface_;
+
+ // Most recent IOSurface that contains a capture of matching display.
+ std::map<CGDirectDisplayID, std::unique_ptr<SharedDesktopFrame>> io_surfaces_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_PROVIDER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm
new file mode 100644
index 0000000000..009504a22b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 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 "modules/desktop_capture/mac/desktop_frame_provider.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/mac/desktop_frame_cgimage.h"
+#include "modules/desktop_capture/mac/desktop_frame_iosurface.h"
+
+namespace webrtc {
+
+DesktopFrameProvider::DesktopFrameProvider(bool allow_iosurface)
+ : allow_iosurface_(allow_iosurface) {
+ thread_checker_.Detach();
+}
+
+DesktopFrameProvider::~DesktopFrameProvider() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ Release();
+}
+
+std::unique_ptr<DesktopFrame> DesktopFrameProvider::TakeLatestFrameForDisplay(
+ CGDirectDisplayID display_id) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ if (!allow_iosurface_ || !io_surfaces_[display_id]) {
+ // Regenerate a snapshot. If iosurface is on it will be empty until the
+ // stream handler is called.
+ return DesktopFrameCGImage::CreateForDisplay(display_id);
+ }
+
+ return io_surfaces_[display_id]->Share();
+}
+
+void DesktopFrameProvider::InvalidateIOSurface(CGDirectDisplayID display_id,
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ if (!allow_iosurface_) {
+ return;
+ }
+
+ std::unique_ptr<DesktopFrameIOSurface> desktop_frame_iosurface =
+ DesktopFrameIOSurface::Wrap(io_surface);
+
+ io_surfaces_[display_id] = desktop_frame_iosurface ?
+ SharedDesktopFrame::Wrap(std::move(desktop_frame_iosurface)) :
+ nullptr;
+}
+
+void DesktopFrameProvider::Release() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ if (!allow_iosurface_) {
+ return;
+ }
+
+ io_surfaces_.clear();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc
new file mode 100644
index 0000000000..45cd3223d2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2019 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 "modules/desktop_capture/mac/full_screen_mac_application_handler.h"
+
+#include <libproc.h>
+
+#include <algorithm>
+#include <functional>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+#include "api/function_view.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+
+namespace webrtc {
+namespace {
+
+static constexpr const char* kPowerPointSlideShowTitles[] = {
+ "PowerPoint-Bildschirmpräsentation",
+ "Προβολή παρουσίασης PowerPoint",
+ "PowerPoint スライド ショー",
+ "PowerPoint Slide Show",
+ "PowerPoint 幻灯片放映",
+ "Presentación de PowerPoint",
+ "PowerPoint-slideshow",
+ "Presentazione di PowerPoint",
+ "Prezentácia programu PowerPoint",
+ "Apresentação do PowerPoint",
+ "PowerPoint-bildspel",
+ "Prezentace v aplikaci PowerPoint",
+ "PowerPoint 슬라이드 쇼",
+ "PowerPoint-lysbildefremvisning",
+ "PowerPoint-vetítés",
+ "PowerPoint Slayt Gösterisi",
+ "Pokaz slajdów programu PowerPoint",
+ "PowerPoint 投影片放映",
+ "Демонстрация PowerPoint",
+ "Diaporama PowerPoint",
+ "PowerPoint-diaesitys",
+ "Peragaan Slide PowerPoint",
+ "PowerPoint-diavoorstelling",
+ "การนำเสนอสไลด์ PowerPoint",
+ "Apresentação de slides do PowerPoint",
+ "הצגת שקופיות של PowerPoint",
+ "عرض شرائح في PowerPoint"};
+
+class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
+ public:
+ using TitlePredicate =
+ std::function<bool(absl::string_view, absl::string_view)>;
+
+ FullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId,
+ TitlePredicate title_predicate,
+ bool ignore_original_window)
+ : FullScreenApplicationHandler(sourceId),
+ title_predicate_(title_predicate),
+ owner_pid_(GetWindowOwnerPid(sourceId)),
+ ignore_original_window_(ignore_original_window) {}
+
+ protected:
+ using CachePredicate =
+ rtc::FunctionView<bool(const DesktopCapturer::Source&)>;
+
+ void InvalidateCacheIfNeeded(const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp,
+ CachePredicate predicate) const {
+ if (timestamp != cache_timestamp_) {
+ cache_sources_.clear();
+ std::copy_if(source_list.begin(), source_list.end(),
+ std::back_inserter(cache_sources_), predicate);
+ cache_timestamp_ = timestamp;
+ }
+ }
+
+ WindowId FindFullScreenWindowWithSamePid(
+ const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp) const {
+ InvalidateCacheIfNeeded(source_list, timestamp,
+ [&](const DesktopCapturer::Source& src) {
+ return src.id != GetSourceId() &&
+ GetWindowOwnerPid(src.id) == owner_pid_;
+ });
+ if (cache_sources_.empty())
+ return kCGNullWindowID;
+
+ const auto original_window = GetSourceId();
+ const std::string title = GetWindowTitle(original_window);
+
+ // We can ignore any windows with empty titles cause regardless type of
+ // application it's impossible to verify that full screen window and
+ // original window are related to the same document.
+ if (title.empty())
+ return kCGNullWindowID;
+
+ MacDesktopConfiguration desktop_config =
+ MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+
+ const auto it = std::find_if(
+ cache_sources_.begin(), cache_sources_.end(),
+ [&](const DesktopCapturer::Source& src) {
+ const std::string window_title = GetWindowTitle(src.id);
+
+ if (window_title.empty())
+ return false;
+
+ if (title_predicate_ && !title_predicate_(title, window_title))
+ return false;
+
+ return IsWindowFullScreen(desktop_config, src.id);
+ });
+
+ return it != cache_sources_.end() ? it->id : 0;
+ }
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp) const override {
+ return !ignore_original_window_ && IsWindowOnScreen(GetSourceId())
+ ? 0
+ : FindFullScreenWindowWithSamePid(source_list, timestamp);
+ }
+
+ protected:
+ const TitlePredicate title_predicate_;
+ const int owner_pid_;
+ const bool ignore_original_window_;
+ mutable int64_t cache_timestamp_ = 0;
+ mutable DesktopCapturer::SourceList cache_sources_;
+};
+
+bool equal_title_predicate(absl::string_view original_title,
+ absl::string_view title) {
+ return original_title == title;
+}
+
+bool slide_show_title_predicate(absl::string_view original_title,
+ absl::string_view title) {
+ if (title.find(original_title) == absl::string_view::npos)
+ return false;
+
+ for (const char* pp_slide_title : kPowerPointSlideShowTitles) {
+ if (absl::StartsWith(title, pp_slide_title))
+ return true;
+ }
+ return false;
+}
+
+class OpenOfficeApplicationHandler : public FullScreenMacApplicationHandler {
+ public:
+ OpenOfficeApplicationHandler(DesktopCapturer::SourceId sourceId)
+ : FullScreenMacApplicationHandler(sourceId, nullptr, false) {}
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp) const override {
+ InvalidateCacheIfNeeded(source_list, timestamp,
+ [&](const DesktopCapturer::Source& src) {
+ return GetWindowOwnerPid(src.id) == owner_pid_;
+ });
+
+ const auto original_window = GetSourceId();
+ const std::string original_title = GetWindowTitle(original_window);
+
+ // Check if we have only one document window, otherwise it's not possible
+ // to securely match a document window and a slide show window which has
+ // empty title.
+ if (std::any_of(cache_sources_.begin(), cache_sources_.end(),
+ [&original_title](const DesktopCapturer::Source& src) {
+ return src.title.length() && src.title != original_title;
+ })) {
+ return kCGNullWindowID;
+ }
+
+ MacDesktopConfiguration desktop_config =
+ MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+
+ // Looking for slide show window,
+ // it must be a full screen window with empty title
+ const auto slide_show_window = std::find_if(
+ cache_sources_.begin(), cache_sources_.end(), [&](const auto& src) {
+ return src.title.empty() &&
+ IsWindowFullScreen(desktop_config, src.id);
+ });
+
+ if (slide_show_window == cache_sources_.end()) {
+ return kCGNullWindowID;
+ }
+
+ return slide_show_window->id;
+ }
+};
+
+} // namespace
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId) {
+ std::unique_ptr<FullScreenApplicationHandler> result;
+ int pid = GetWindowOwnerPid(sourceId);
+ char buffer[PROC_PIDPATHINFO_MAXSIZE];
+ int path_length = proc_pidpath(pid, buffer, sizeof(buffer));
+ if (path_length > 0) {
+ const char* last_slash = strrchr(buffer, '/');
+ const std::string name{last_slash ? last_slash + 1 : buffer};
+ const std::string owner_name = GetWindowOwnerName(sourceId);
+ FullScreenMacApplicationHandler::TitlePredicate predicate = nullptr;
+ bool ignore_original_window = false;
+ if (name.find("Google Chrome") == 0 || name == "Chromium") {
+ predicate = equal_title_predicate;
+ } else if (name == "Microsoft PowerPoint") {
+ predicate = slide_show_title_predicate;
+ ignore_original_window = true;
+ } else if (name == "Keynote") {
+ predicate = equal_title_predicate;
+ } else if (owner_name == "OpenOffice") {
+ return std::make_unique<OpenOfficeApplicationHandler>(sourceId);
+ }
+
+ if (predicate) {
+ result.reset(new FullScreenMacApplicationHandler(sourceId, predicate,
+ ignore_original_window));
+ }
+ }
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h
new file mode 100644
index 0000000000..f795a22030
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_MAC_APPLICATION_HANDLER_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_MAC_APPLICATION_HANDLER_H_
+
+#include <memory>
+#include "modules/desktop_capture/full_screen_application_handler.h"
+
+namespace webrtc {
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_MAC_APPLICATION_HANDLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h
new file mode 100644
index 0000000000..7be05cc639
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_SCREEN_CAPTURER_MAC_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_SCREEN_CAPTURER_MAC_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#include <memory>
+#include <vector>
+
+#include "api/sequence_checker.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/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.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_provider.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+
+namespace webrtc {
+
+class DisplayStreamManager;
+
+// A class to perform video frame capturing for mac.
+class ScreenCapturerMac final : public DesktopCapturer {
+ public:
+ ScreenCapturerMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor,
+ bool detect_updated_region,
+ bool allow_iosurface);
+ ~ScreenCapturerMac() override;
+
+ ScreenCapturerMac(const ScreenCapturerMac&) = delete;
+ ScreenCapturerMac& operator=(const ScreenCapturerMac&) = delete;
+
+ // TODO(julien.isorce): Remove Init() or make it private.
+ bool Init();
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ // Returns false if the selected screen is no longer valid.
+ bool CgBlit(const DesktopFrame& frame, const DesktopRegion& region);
+
+ // Called when the screen configuration is changed.
+ void ScreenConfigurationChanged();
+
+ bool RegisterRefreshAndMoveHandlers();
+ void UnregisterRefreshAndMoveHandlers();
+
+ void ScreenRefresh(CGDirectDisplayID display_id,
+ CGRectCount count,
+ const CGRect* rect_array,
+ DesktopVector display_origin,
+ IOSurfaceRef io_surface);
+ void ReleaseBuffers();
+
+ std::unique_ptr<DesktopFrame> CreateFrame();
+
+ const bool detect_updated_region_;
+
+ Callback* callback_ = nullptr;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ // Current display configuration.
+ MacDesktopConfiguration desktop_config_;
+
+ // Currently selected display, or 0 if the full desktop is selected. On OS X
+ // 10.6 and before, this is always 0.
+ CGDirectDisplayID current_display_ = 0;
+
+ // The physical pixel bounds of the current screen.
+ DesktopRect screen_pixel_bounds_;
+
+ // The dip to physical pixel scale of the current screen.
+ float dip_to_pixel_scale_ = 1.0f;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ // Contains an invalid region from the previous capture.
+ DesktopRegion last_invalid_region_;
+
+ // Monitoring display reconfiguration.
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
+
+ CGWindowID excluded_window_ = 0;
+
+ // List of streams, one per screen.
+ std::vector<CGDisplayStreamRef> display_streams_;
+
+ // Container holding latest state of the snapshot per displays.
+ DesktopFrameProvider desktop_frame_provider_;
+
+ // Start, CaptureFrame and destructor have to called in the same thread.
+ SequenceChecker thread_checker_;
+
+ // Used to force CaptureFrame to update it's screen configuration
+ // and reregister event handlers. This ensure that this
+ // occurs on the ScreenCapture thread. Read and written from
+ // both the VideoCapture thread and ScreenCapture thread.
+ // Protected by desktop_config_monitor_.
+ bool update_screen_configuration_ = false;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_SCREEN_CAPTURER_MAC_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
new file mode 100644
index 0000000000..76fec13a39
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
@@ -0,0 +1,633 @@
+/*
+ * 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 <utility>
+
+#include "modules/desktop_capture/mac/screen_capturer_mac.h"
+
+#include "modules/desktop_capture/mac/desktop_frame_provider.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+// All these symbols have incorrect availability annotations in the 13.3 SDK.
+// These have the correct annotation. See https://crbug.com/1431897.
+// TODO(thakis): Remove this once FB12109479 is fixed and we updated to an SDK
+// with the fix.
+
+static CGDisplayStreamRef __nullable
+ wrapCGDisplayStreamCreate(CGDirectDisplayID display,
+ size_t outputWidth,
+ size_t outputHeight,
+ int32_t pixelFormat,
+ CFDictionaryRef __nullable properties,
+ CGDisplayStreamFrameAvailableHandler __nullable handler)
+ CG_AVAILABLE_BUT_DEPRECATED(
+ 10.8,
+ 14.0,
+ "Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead") {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ return CGDisplayStreamCreate(
+ display, outputWidth, outputHeight, pixelFormat, properties, handler);
+#pragma clang diagnostic pop
+}
+
+static CFRunLoopSourceRef __nullable
+ wrapCGDisplayStreamGetRunLoopSource(CGDisplayStreamRef cg_nullable displayStream)
+ CG_AVAILABLE_BUT_DEPRECATED(10.8,
+ 14.0,
+ "There is no direct replacement for this function. Please use "
+ "ScreenCaptureKit API's SCStream to replace CGDisplayStream") {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ return CGDisplayStreamGetRunLoopSource(displayStream);
+#pragma clang diagnostic pop
+}
+
+static CGError wrapCGDisplayStreamStart(CGDisplayStreamRef cg_nullable displayStream)
+ CG_AVAILABLE_BUT_DEPRECATED(10.8,
+ 14.0,
+ "Please use ScreenCaptureKit API's "
+ "startCaptureWithCompletionHandler: to start a stream instead") {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ return CGDisplayStreamStart(displayStream);
+#pragma clang diagnostic pop
+}
+
+static CGError wrapCGDisplayStreamStop(CGDisplayStreamRef cg_nullable displayStream)
+ CG_AVAILABLE_BUT_DEPRECATED(10.8,
+ 14.0,
+ "Please use ScreenCaptureKit API's "
+ "stopCaptureWithCompletionHandler: to stop a stream instead") {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ return CGDisplayStreamStop(displayStream);
+#pragma clang diagnostic pop
+}
+
+static CFStringRef wrapkCGDisplayStreamShowCursor() CG_AVAILABLE_BUT_DEPRECATED(
+ 10.8,
+ 14.0,
+ "Please use ScreenCaptureKit API's SCStreamConfiguration showsCursor property instead") {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ return kCGDisplayStreamShowCursor;
+#pragma clang diagnostic pop
+}
+
+static const CGRect* __nullable
+ wrapCGDisplayStreamUpdateGetRects(CGDisplayStreamUpdateRef __nullable updateRef,
+ CGDisplayStreamUpdateRectType rectType,
+ size_t* rectCount)
+ CG_AVAILABLE_BUT_DEPRECATED(10.8,
+ 14.0,
+ "Please use ScreenCaptureKit API's SCStreamFrameInfo with "
+ "SCStreamFrameInfoContentRect instead") {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ return CGDisplayStreamUpdateGetRects(updateRef, rectType, rectCount);
+#pragma clang diagnostic pop
+}
+
+namespace webrtc {
+
+namespace {
+
+// Scales all coordinates of a rect by a specified factor.
+DesktopRect ScaleAndRoundCGRect(const CGRect& rect, float scale) {
+ return DesktopRect::MakeLTRB(static_cast<int>(floor(rect.origin.x * scale)),
+ static_cast<int>(floor(rect.origin.y * scale)),
+ static_cast<int>(ceil((rect.origin.x + rect.size.width) * scale)),
+ static_cast<int>(ceil((rect.origin.y + rect.size.height) * scale)));
+}
+
+// Copy pixels in the `rect` from `src_place` to `dest_plane`. `rect` should be
+// relative to the origin of `src_plane` and `dest_plane`.
+void CopyRect(const uint8_t* src_plane,
+ int src_plane_stride,
+ uint8_t* dest_plane,
+ int dest_plane_stride,
+ int bytes_per_pixel,
+ const DesktopRect& rect) {
+ // Get the address of the starting point.
+ const int src_y_offset = src_plane_stride * rect.top();
+ const int dest_y_offset = dest_plane_stride * rect.top();
+ const int x_offset = bytes_per_pixel * rect.left();
+ src_plane += src_y_offset + x_offset;
+ dest_plane += dest_y_offset + x_offset;
+
+ // Copy pixels in the rectangle line by line.
+ const int bytes_per_line = bytes_per_pixel * rect.width();
+ const int height = rect.height();
+ for (int i = 0; i < height; ++i) {
+ memcpy(dest_plane, src_plane, bytes_per_line);
+ src_plane += src_plane_stride;
+ dest_plane += dest_plane_stride;
+ }
+}
+
+// Returns an array of CGWindowID for all the on-screen windows except
+// `window_to_exclude`, or NULL if the window is not found or it fails. The
+// caller should release the returned CFArrayRef.
+CFArrayRef CreateWindowListWithExclusion(CGWindowID window_to_exclude) {
+ if (!window_to_exclude) return nullptr;
+
+ CFArrayRef all_windows =
+ CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
+ if (!all_windows) return nullptr;
+
+ CFMutableArrayRef returned_array =
+ CFArrayCreateMutable(nullptr, CFArrayGetCount(all_windows), nullptr);
+
+ bool found = false;
+ for (CFIndex i = 0; i < CFArrayGetCount(all_windows); ++i) {
+ CFDictionaryRef window =
+ reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(all_windows, i));
+
+ CGWindowID id = GetWindowId(window);
+ if (id == window_to_exclude) {
+ found = true;
+ continue;
+ }
+ CFArrayAppendValue(returned_array, reinterpret_cast<void*>(id));
+ }
+ CFRelease(all_windows);
+
+ if (!found) {
+ CFRelease(returned_array);
+ returned_array = nullptr;
+ }
+ return returned_array;
+}
+
+// Returns the bounds of `window` in physical pixels, enlarged by a small amount
+// on four edges to take account of the border/shadow effects.
+DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_scale) {
+ // The amount of pixels to add to the actual window bounds to take into
+ // account of the border/shadow effects.
+ static const int kBorderEffectSize = 20;
+ CGRect rect;
+ CGWindowID ids[1];
+ ids[0] = window;
+
+ CFArrayRef window_id_array =
+ CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
+ CFArrayRef window_array = CGWindowListCreateDescriptionFromArray(window_id_array);
+
+ if (CFArrayGetCount(window_array) > 0) {
+ CFDictionaryRef win =
+ reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(window_array, 0));
+ CFDictionaryRef bounds_ref =
+ reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(win, kCGWindowBounds));
+ CGRectMakeWithDictionaryRepresentation(bounds_ref, &rect);
+ }
+
+ CFRelease(window_id_array);
+ CFRelease(window_array);
+
+ rect.origin.x -= kBorderEffectSize;
+ rect.origin.y -= kBorderEffectSize;
+ rect.size.width += kBorderEffectSize * 2;
+ rect.size.height += kBorderEffectSize * 2;
+ // `rect` is in DIP, so convert to physical pixels.
+ return ScaleAndRoundCGRect(rect, dip_to_pixel_scale);
+}
+
+// Create an image of the given region using the given `window_list`.
+// `pixel_bounds` should be in the primary display's coordinate in physical
+// pixels.
+rtc::ScopedCFTypeRef<CGImageRef> CreateExcludedWindowRegionImage(const DesktopRect& pixel_bounds,
+ float dip_to_pixel_scale,
+ CFArrayRef window_list) {
+ CGRect window_bounds;
+ // The origin is in DIP while the size is in physical pixels. That's what
+ // CGWindowListCreateImageFromArray expects.
+ window_bounds.origin.x = pixel_bounds.left() / dip_to_pixel_scale;
+ window_bounds.origin.y = pixel_bounds.top() / dip_to_pixel_scale;
+ window_bounds.size.width = pixel_bounds.width();
+ window_bounds.size.height = pixel_bounds.height();
+
+ return rtc::ScopedCFTypeRef<CGImageRef>(
+ CGWindowListCreateImageFromArray(window_bounds, window_list, kCGWindowImageDefault));
+}
+
+} // namespace
+
+ScreenCapturerMac::ScreenCapturerMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor,
+ bool detect_updated_region,
+ bool allow_iosurface)
+ : detect_updated_region_(detect_updated_region),
+ desktop_config_monitor_(desktop_config_monitor),
+ desktop_frame_provider_(allow_iosurface) {
+ RTC_LOG(LS_INFO) << "Allow IOSurface: " << allow_iosurface;
+ thread_checker_.Detach();
+}
+
+ScreenCapturerMac::~ScreenCapturerMac() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ ReleaseBuffers();
+ UnregisterRefreshAndMoveHandlers();
+}
+
+bool ScreenCapturerMac::Init() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerMac::Init");
+ desktop_config_ = desktop_config_monitor_->desktop_configuration();
+ return true;
+}
+
+void ScreenCapturerMac::ReleaseBuffers() {
+ // The buffers might be in use by the encoder, so don't delete them here.
+ // Instead, mark them as "needs update"; next time the buffers are used by
+ // the capturer, they will be recreated if necessary.
+ queue_.Reset();
+}
+
+void ScreenCapturerMac::Start(Callback* callback) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ TRACE_EVENT_INSTANT1(
+ "webrtc", "ScreenCapturermac::Start", "target display id ", current_display_);
+
+ callback_ = callback;
+ update_screen_configuration_ = false;
+ // Start and operate CGDisplayStream handler all from capture thread.
+ if (!RegisterRefreshAndMoveHandlers()) {
+ RTC_LOG(LS_ERROR) << "Failed to register refresh and move handlers.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ ScreenConfigurationChanged();
+}
+
+void ScreenCapturerMac::CaptureFrame() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ TRACE_EVENT0("webrtc", "creenCapturerMac::CaptureFrame");
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
+ }
+
+ MacDesktopConfiguration new_config = desktop_config_monitor_->desktop_configuration();
+ if (update_screen_configuration_ || !desktop_config_.Equals(new_config)) {
+ update_screen_configuration_ = false;
+ desktop_config_ = new_config;
+ // If the display configuraiton has changed then refresh capturer data
+ // structures. Occasionally, the refresh and move handlers are lost when
+ // the screen mode changes, so re-register them here.
+ UnregisterRefreshAndMoveHandlers();
+ if (!RegisterRefreshAndMoveHandlers()) {
+ RTC_LOG(LS_ERROR) << "Failed to register refresh and move handlers.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ ScreenConfigurationChanged();
+ }
+
+ // When screen is zoomed in/out, OSX only updates the part of Rects currently
+ // displayed on screen, with relative location to current top-left on screen.
+ // This will cause problems when we copy the dirty regions to the captured
+ // image. So we invalidate the whole screen to copy all the screen contents.
+ // With CGI method, the zooming will be ignored and the whole screen contents
+ // will be captured as before.
+ // With IOSurface method, the zoomed screen contents will be captured.
+ if (UAZoomEnabled()) {
+ helper_.InvalidateScreen(screen_pixel_bounds_.size());
+ }
+
+ DesktopRegion region;
+ helper_.TakeInvalidRegion(&region);
+
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame()) queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(CreateFrame()));
+
+ DesktopFrame* current_frame = queue_.current_frame();
+
+ if (!CgBlit(*current_frame, region)) {
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ std::unique_ptr<DesktopFrame> new_frame = queue_.current_frame()->Share();
+ if (detect_updated_region_) {
+ *new_frame->mutable_updated_region() = region;
+ } else {
+ new_frame->mutable_updated_region()->AddRect(DesktopRect::MakeSize(new_frame->size()));
+ }
+
+ if (current_display_) {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ if (config) {
+ new_frame->set_top_left(
+ config->bounds.top_left().subtract(desktop_config_.bounds.top_left()));
+ }
+ }
+
+ helper_.set_size_most_recent(new_frame->size());
+
+ new_frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(new_frame));
+}
+
+void ScreenCapturerMac::SetExcludedWindow(WindowId window) {
+ excluded_window_ = window;
+}
+
+bool ScreenCapturerMac::GetSourceList(SourceList* screens) {
+ RTC_DCHECK(screens->size() == 0);
+
+ for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
+ it != desktop_config_.displays.end();
+ ++it) {
+ Source value = {it->id, 0, std::string()};
+ screens->push_back(value);
+ }
+ return true;
+}
+
+bool ScreenCapturerMac::SelectSource(SourceId id) {
+ if (id == kFullDesktopScreenId) {
+ current_display_ = 0;
+ } else {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(static_cast<CGDirectDisplayID>(id));
+ if (!config) return false;
+ current_display_ = config->id;
+ }
+
+ ScreenConfigurationChanged();
+ return true;
+}
+
+bool ScreenCapturerMac::CgBlit(const DesktopFrame& frame, const DesktopRegion& region) {
+ // If not all screen region is dirty, copy the entire contents of the previous capture buffer,
+ // to capture over.
+ if (queue_.previous_frame() && !region.Equals(DesktopRegion(screen_pixel_bounds_))) {
+ memcpy(frame.data(), queue_.previous_frame()->data(), frame.stride() * frame.size().height());
+ }
+
+ MacDisplayConfigurations displays_to_capture;
+ if (current_display_) {
+ // Capturing a single screen. Note that the screen id may change when
+ // screens are added or removed.
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ if (config) {
+ displays_to_capture.push_back(*config);
+ } else {
+ RTC_LOG(LS_ERROR) << "The selected screen cannot be found for capturing.";
+ return false;
+ }
+ } else {
+ // Capturing the whole desktop.
+ displays_to_capture = desktop_config_.displays;
+ }
+
+ // Create the window list once for all displays.
+ CFArrayRef window_list = CreateWindowListWithExclusion(excluded_window_);
+
+ for (size_t i = 0; i < displays_to_capture.size(); ++i) {
+ const MacDisplayConfiguration& display_config = displays_to_capture[i];
+
+ // Capturing mixed-DPI on one surface is hard, so we only return displays
+ // that match the "primary" display's DPI. The primary display is always
+ // the first in the list.
+ if (i > 0 && display_config.dip_to_pixel_scale != displays_to_capture[0].dip_to_pixel_scale) {
+ continue;
+ }
+ // Determine the display's position relative to the desktop, in pixels.
+ DesktopRect display_bounds = display_config.pixel_bounds;
+ display_bounds.Translate(-screen_pixel_bounds_.left(), -screen_pixel_bounds_.top());
+
+ // Determine which parts of the blit region, if any, lay within the monitor.
+ DesktopRegion copy_region = region;
+ copy_region.IntersectWith(display_bounds);
+ if (copy_region.is_empty()) continue;
+
+ // Translate the region to be copied into display-relative coordinates.
+ copy_region.Translate(-display_bounds.left(), -display_bounds.top());
+
+ DesktopRect excluded_window_bounds;
+ rtc::ScopedCFTypeRef<CGImageRef> excluded_image;
+ if (excluded_window_ && window_list) {
+ // Get the region of the excluded window relative the primary display.
+ excluded_window_bounds =
+ GetExcludedWindowPixelBounds(excluded_window_, display_config.dip_to_pixel_scale);
+ excluded_window_bounds.IntersectWith(display_config.pixel_bounds);
+
+ // Create the image under the excluded window first, because it's faster
+ // than captuing the whole display.
+ if (!excluded_window_bounds.is_empty()) {
+ excluded_image = CreateExcludedWindowRegionImage(
+ excluded_window_bounds, display_config.dip_to_pixel_scale, window_list);
+ }
+ }
+
+ std::unique_ptr<DesktopFrame> frame_source =
+ desktop_frame_provider_.TakeLatestFrameForDisplay(display_config.id);
+ if (!frame_source) {
+ continue;
+ }
+
+ const uint8_t* display_base_address = frame_source->data();
+ int src_bytes_per_row = frame_source->stride();
+ RTC_DCHECK(display_base_address);
+
+ // `frame_source` size may be different from display_bounds in case the screen was
+ // resized recently.
+ copy_region.IntersectWith(frame_source->rect());
+
+ // Copy the dirty region from the display buffer into our desktop buffer.
+ uint8_t* out_ptr = frame.GetFrameDataAtPos(display_bounds.top_left());
+ for (DesktopRegion::Iterator it(copy_region); !it.IsAtEnd(); it.Advance()) {
+ CopyRect(display_base_address,
+ src_bytes_per_row,
+ out_ptr,
+ frame.stride(),
+ DesktopFrame::kBytesPerPixel,
+ it.rect());
+ }
+
+ if (excluded_image) {
+ CGDataProviderRef provider = CGImageGetDataProvider(excluded_image.get());
+ rtc::ScopedCFTypeRef<CFDataRef> excluded_image_data(CGDataProviderCopyData(provider));
+ RTC_DCHECK(excluded_image_data);
+ display_base_address = CFDataGetBytePtr(excluded_image_data.get());
+ src_bytes_per_row = CGImageGetBytesPerRow(excluded_image.get());
+
+ // Translate the bounds relative to the desktop, because `frame` data
+ // starts from the desktop top-left corner.
+ DesktopRect window_bounds_relative_to_desktop(excluded_window_bounds);
+ window_bounds_relative_to_desktop.Translate(-screen_pixel_bounds_.left(),
+ -screen_pixel_bounds_.top());
+
+ DesktopRect rect_to_copy = DesktopRect::MakeSize(excluded_window_bounds.size());
+ rect_to_copy.IntersectWith(DesktopRect::MakeWH(CGImageGetWidth(excluded_image.get()),
+ CGImageGetHeight(excluded_image.get())));
+
+ if (CGImageGetBitsPerPixel(excluded_image.get()) / 8 == DesktopFrame::kBytesPerPixel) {
+ CopyRect(display_base_address,
+ src_bytes_per_row,
+ frame.GetFrameDataAtPos(window_bounds_relative_to_desktop.top_left()),
+ frame.stride(),
+ DesktopFrame::kBytesPerPixel,
+ rect_to_copy);
+ }
+ }
+ }
+ if (window_list) CFRelease(window_list);
+ return true;
+}
+
+void ScreenCapturerMac::ScreenConfigurationChanged() {
+ if (current_display_) {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ screen_pixel_bounds_ = config ? config->pixel_bounds : DesktopRect();
+ dip_to_pixel_scale_ = config ? config->dip_to_pixel_scale : 1.0f;
+ } else {
+ screen_pixel_bounds_ = desktop_config_.pixel_bounds;
+ dip_to_pixel_scale_ = desktop_config_.dip_to_pixel_scale;
+ }
+
+ // Release existing buffers, which will be of the wrong size.
+ ReleaseBuffers();
+
+ // Clear the dirty region, in case the display is down-sizing.
+ helper_.ClearInvalidRegion();
+
+ // Re-mark the entire desktop as dirty.
+ helper_.InvalidateScreen(screen_pixel_bounds_.size());
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+}
+
+bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ desktop_config_ = desktop_config_monitor_->desktop_configuration();
+ for (const auto& config : desktop_config_.displays) {
+ size_t pixel_width = config.pixel_bounds.width();
+ size_t pixel_height = config.pixel_bounds.height();
+ if (pixel_width == 0 || pixel_height == 0) continue;
+ CGDirectDisplayID display_id = config.id;
+ DesktopVector display_origin = config.pixel_bounds.top_left();
+
+ CGDisplayStreamFrameAvailableHandler handler = ^(CGDisplayStreamFrameStatus status,
+ uint64_t display_time,
+ IOSurfaceRef frame_surface,
+ CGDisplayStreamUpdateRef updateRef) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ if (status == kCGDisplayStreamFrameStatusStopped) return;
+
+ // Only pay attention to frame updates.
+ if (status != kCGDisplayStreamFrameStatusFrameComplete) return;
+
+ size_t count = 0;
+ const CGRect* rects =
+ wrapCGDisplayStreamUpdateGetRects(updateRef, kCGDisplayStreamUpdateDirtyRects, &count);
+ if (count != 0) {
+ // According to CGDisplayStream.h, it's safe to call
+ // CGDisplayStreamStop() from within the callback.
+ ScreenRefresh(display_id, count, rects, display_origin, frame_surface);
+ }
+ };
+
+ rtc::ScopedCFTypeRef<CFDictionaryRef> properties_dict(
+ CFDictionaryCreate(kCFAllocatorDefault,
+ (const void*[]){wrapkCGDisplayStreamShowCursor()},
+ (const void*[]){kCFBooleanFalse},
+ 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ CGDisplayStreamRef display_stream = wrapCGDisplayStreamCreate(
+ display_id, pixel_width, pixel_height, 'BGRA', properties_dict.get(), handler);
+
+ if (display_stream) {
+ CGError error = wrapCGDisplayStreamStart(display_stream);
+ if (error != kCGErrorSuccess) return false;
+
+ CFRunLoopSourceRef source = wrapCGDisplayStreamGetRunLoopSource(display_stream);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
+ display_streams_.push_back(display_stream);
+ }
+ }
+
+ return true;
+}
+
+void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ for (CGDisplayStreamRef stream : display_streams_) {
+ CFRunLoopSourceRef source = wrapCGDisplayStreamGetRunLoopSource(stream);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
+ wrapCGDisplayStreamStop(stream);
+ CFRelease(stream);
+ }
+ display_streams_.clear();
+
+ // Release obsolete io surfaces.
+ desktop_frame_provider_.Release();
+}
+
+void ScreenCapturerMac::ScreenRefresh(CGDirectDisplayID display_id,
+ CGRectCount count,
+ const CGRect* rect_array,
+ DesktopVector display_origin,
+ IOSurfaceRef io_surface) {
+ if (screen_pixel_bounds_.is_empty()) ScreenConfigurationChanged();
+
+ // The refresh rects are in display coordinates. We want to translate to
+ // framebuffer coordinates. If a specific display is being captured, then no
+ // change is necessary. If all displays are being captured, then we want to
+ // translate by the origin of the display.
+ DesktopVector translate_vector;
+ if (!current_display_) translate_vector = display_origin;
+
+ DesktopRegion region;
+ for (CGRectCount i = 0; i < count; ++i) {
+ // All rects are already in physical pixel coordinates.
+ DesktopRect rect = DesktopRect::MakeXYWH(rect_array[i].origin.x,
+ rect_array[i].origin.y,
+ rect_array[i].size.width,
+ rect_array[i].size.height);
+
+ rect.Translate(translate_vector);
+
+ region.AddRect(rect);
+ }
+ // Always having the latest iosurface before invalidating a region.
+ // See https://bugs.chromium.org/p/webrtc/issues/detail?id=8652 for details.
+ desktop_frame_provider_.InvalidateIOSurface(
+ display_id, rtc::ScopedCFTypeRef<IOSurfaceRef>(io_surface, rtc::RetainPolicy::RETAIN));
+ helper_.InvalidateRegion(region);
+}
+
+std::unique_ptr<DesktopFrame> ScreenCapturerMac::CreateFrame() {
+ std::unique_ptr<DesktopFrame> frame(new BasicDesktopFrame(screen_pixel_bounds_.size()));
+ frame->set_dpi(
+ DesktopVector(kStandardDPI * dip_to_pixel_scale_, kStandardDPI * dip_to_pixel_scale_));
+ return frame;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc
new file mode 100644
index 0000000000..989ec7ea54
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2014 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 "modules/desktop_capture/mac/window_list_utils.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <limits>
+#include <list>
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "rtc_base/checks.h"
+
+static_assert(static_cast<webrtc::WindowId>(kCGNullWindowID) ==
+ webrtc::kNullWindowId,
+ "kNullWindowId needs to equal to kCGNullWindowID.");
+
+namespace webrtc {
+
+namespace {
+
+// WindowName of the status indicator dot shown since Monterey in the taskbar.
+// Testing on 12.2.1 shows this is independent of system language setting.
+const CFStringRef kStatusIndicator = CFSTR("StatusIndicator");
+const CFStringRef kStatusIndicatorOwnerName = CFSTR("Window Server");
+
+bool ToUtf8(const CFStringRef str16, std::string* str8) {
+ size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16),
+ kCFStringEncodingUTF8) +
+ 1;
+ std::unique_ptr<char[]> buffer(new char[maxlen]);
+ if (!buffer ||
+ !CFStringGetCString(str16, buffer.get(), maxlen, kCFStringEncodingUTF8)) {
+ return false;
+ }
+ str8->assign(buffer.get());
+ return true;
+}
+
+// Get CFDictionaryRef from `id` and call `on_window` against it. This function
+// returns false if native APIs fail, typically it indicates that the `id` does
+// not represent a window. `on_window` will not be called if false is returned
+// from this function.
+bool GetWindowRef(CGWindowID id,
+ rtc::FunctionView<void(CFDictionaryRef)> on_window) {
+ RTC_DCHECK(on_window);
+
+ // TODO(zijiehe): `id` is a 32-bit integer, casting it to an array seems not
+ // safe enough. Maybe we should create a new
+ // const void* arr[] = {
+ // reinterpret_cast<void*>(id) }
+ // };
+ CFArrayRef window_id_array =
+ CFArrayCreate(NULL, reinterpret_cast<const void**>(&id), 1, NULL);
+ CFArrayRef window_array =
+ CGWindowListCreateDescriptionFromArray(window_id_array);
+
+ bool result = false;
+ // TODO(zijiehe): CFArrayGetCount(window_array) should always return 1.
+ // Otherwise, we should treat it as failure.
+ if (window_array && CFArrayGetCount(window_array)) {
+ on_window(reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, 0)));
+ result = true;
+ }
+
+ if (window_array) {
+ CFRelease(window_array);
+ }
+ CFRelease(window_id_array);
+ return result;
+}
+
+} // namespace
+
+bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
+ bool ignore_minimized,
+ bool only_zero_layer) {
+ RTC_DCHECK(on_window);
+
+ // Only get on screen, non-desktop windows.
+ // According to
+ // https://developer.apple.com/documentation/coregraphics/cgwindowlistoption/1454105-optiononscreenonly
+ // , when kCGWindowListOptionOnScreenOnly is used, the order of windows are in
+ // decreasing z-order.
+ CFArrayRef window_array = CGWindowListCopyWindowInfo(
+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
+ kCGNullWindowID);
+ if (!window_array)
+ return false;
+
+ MacDesktopConfiguration desktop_config = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+
+ // Check windows to make sure they have an id, title, and use window layer
+ // other than 0.
+ CFIndex count = CFArrayGetCount(window_array);
+ for (CFIndex i = 0; i < count; i++) {
+ CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, i));
+ if (!window) {
+ continue;
+ }
+
+ CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowNumber));
+ if (!window_id) {
+ continue;
+ }
+
+ CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowLayer));
+ if (!window_layer) {
+ continue;
+ }
+
+ // Skip windows with layer!=0 (menu, dock).
+ int layer;
+ if (!CFNumberGetValue(window_layer, kCFNumberIntType, &layer)) {
+ continue;
+ }
+ if (only_zero_layer && layer != 0) {
+ continue;
+ }
+
+ // Skip windows that are minimized and not full screen.
+ if (ignore_minimized && !IsWindowOnScreen(window) &&
+ !IsWindowFullScreen(desktop_config, window)) {
+ continue;
+ }
+
+ // If window title is empty, only consider it if it is either on screen or
+ // fullscreen.
+ CFStringRef window_title = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowName));
+ if (!window_title && !IsWindowOnScreen(window) &&
+ !IsWindowFullScreen(desktop_config, window)) {
+ continue;
+ }
+
+ CFStringRef window_owner_name = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerName));
+ // Ignore the red dot status indicator shown in the stats bar. Unlike the
+ // rest of the system UI it has a window_layer of 0, so was otherwise
+ // included. See crbug.com/1297731.
+ if (window_title && CFEqual(window_title, kStatusIndicator) &&
+ window_owner_name &&
+ CFEqual(window_owner_name, kStatusIndicatorOwnerName)) {
+ continue;
+ }
+
+ if (!on_window(window)) {
+ break;
+ }
+ }
+
+ CFRelease(window_array);
+ return true;
+}
+
+bool GetWindowList(DesktopCapturer::SourceList* windows,
+ bool ignore_minimized,
+ bool only_zero_layer) {
+ // Use a std::list so that iterators are preversed upon insertion and
+ // deletion.
+ std::list<DesktopCapturer::Source> sources;
+ std::map<int, std::list<DesktopCapturer::Source>::const_iterator> pid_itr_map;
+ const bool ret = GetWindowList(
+ [&sources, &pid_itr_map](CFDictionaryRef window) {
+ WindowId window_id = GetWindowId(window);
+ if (window_id != kNullWindowId) {
+ const std::string title = GetWindowTitle(window);
+ const int pid = GetWindowOwnerPid(window);
+ // Check if window for the same pid have been already inserted.
+ std::map<int,
+ std::list<DesktopCapturer::Source>::const_iterator>::iterator
+ itr = pid_itr_map.find(pid);
+
+ // Only consider empty titles if the app has no other window with a
+ // proper title.
+ if (title.empty()) {
+ std::string owner_name = GetWindowOwnerName(window);
+
+ // At this time we do not know if there will be other windows
+ // for the same pid unless they have been already inserted, hence
+ // the check in the map. Also skip the window if owner name is
+ // empty too.
+ if (!owner_name.empty() && (itr == pid_itr_map.end())) {
+ sources.push_back(DesktopCapturer::Source{window_id, pid, owner_name});
+ RTC_DCHECK(!sources.empty());
+ // Get an iterator on the last valid element in the source list.
+ std::list<DesktopCapturer::Source>::const_iterator last_source =
+ --sources.end();
+ pid_itr_map.insert(
+ std::pair<int,
+ std::list<DesktopCapturer::Source>::const_iterator>(
+ pid, last_source));
+ }
+ } else {
+ sources.push_back(DesktopCapturer::Source{window_id, pid, title});
+ // Once the window with empty title has been removed no other empty
+ // windows are allowed for the same pid.
+ if (itr != pid_itr_map.end() && (itr->second != sources.end())) {
+ sources.erase(itr->second);
+ // sdt::list::end() never changes during the lifetime of that
+ // list.
+ itr->second = sources.end();
+ }
+ }
+ }
+ return true;
+ },
+ ignore_minimized, only_zero_layer);
+
+ if (!ret)
+ return false;
+
+ RTC_DCHECK(windows);
+ windows->reserve(windows->size() + sources.size());
+ std::copy(std::begin(sources), std::end(sources),
+ std::back_inserter(*windows));
+
+ return true;
+}
+
+// Returns true if the window is occupying a full screen.
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CFDictionaryRef window) {
+ bool fullscreen = false;
+ CFDictionaryRef bounds_ref = reinterpret_cast<CFDictionaryRef>(
+ CFDictionaryGetValue(window, kCGWindowBounds));
+
+ CGRect bounds;
+ if (bounds_ref &&
+ CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) {
+ for (MacDisplayConfigurations::const_iterator it =
+ desktop_config.displays.begin();
+ it != desktop_config.displays.end(); it++) {
+ if (it->bounds.equals(
+ DesktopRect::MakeXYWH(bounds.origin.x, bounds.origin.y,
+ bounds.size.width, bounds.size.height))) {
+ fullscreen = true;
+ break;
+ }
+ }
+ }
+
+ return fullscreen;
+}
+
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CGWindowID id) {
+ bool fullscreen = false;
+ GetWindowRef(id, [&](CFDictionaryRef window) {
+ fullscreen = IsWindowFullScreen(desktop_config, window);
+ });
+ return fullscreen;
+}
+
+bool IsWindowOnScreen(CFDictionaryRef window) {
+ CFBooleanRef on_screen = reinterpret_cast<CFBooleanRef>(
+ CFDictionaryGetValue(window, kCGWindowIsOnscreen));
+ return on_screen != NULL && CFBooleanGetValue(on_screen);
+}
+
+bool IsWindowOnScreen(CGWindowID id) {
+ bool on_screen;
+ if (GetWindowRef(id, [&on_screen](CFDictionaryRef window) {
+ on_screen = IsWindowOnScreen(window);
+ })) {
+ return on_screen;
+ }
+ return false;
+}
+
+std::string GetWindowTitle(CFDictionaryRef window) {
+ CFStringRef title = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowName));
+ std::string result;
+ if (title && ToUtf8(title, &result)) {
+ return result;
+ }
+
+ return std::string();
+}
+
+std::string GetWindowTitle(CGWindowID id) {
+ std::string title;
+ if (GetWindowRef(id, [&title](CFDictionaryRef window) {
+ title = GetWindowTitle(window);
+ })) {
+ return title;
+ }
+ return std::string();
+}
+
+std::string GetWindowOwnerName(CFDictionaryRef window) {
+ CFStringRef owner_name = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerName));
+ std::string result;
+ if (owner_name && ToUtf8(owner_name, &result)) {
+ return result;
+ }
+ return std::string();
+}
+
+std::string GetWindowOwnerName(CGWindowID id) {
+ std::string owner_name;
+ if (GetWindowRef(id, [&owner_name](CFDictionaryRef window) {
+ owner_name = GetWindowOwnerName(window);
+ })) {
+ return owner_name;
+ }
+ return std::string();
+}
+
+WindowId GetWindowId(CFDictionaryRef window) {
+ CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowNumber));
+ if (!window_id) {
+ return kNullWindowId;
+ }
+
+ // Note: WindowId is 64-bit on 64-bit system, but CGWindowID is always 32-bit.
+ // CFNumberGetValue() fills only top 32 bits, so we should use CGWindowID to
+ // receive the window id.
+ CGWindowID id;
+ if (!CFNumberGetValue(window_id, kCFNumberIntType, &id)) {
+ return kNullWindowId;
+ }
+
+ return id;
+}
+
+int GetWindowOwnerPid(CFDictionaryRef window) {
+ CFNumberRef window_pid = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerPID));
+ if (!window_pid) {
+ return 0;
+ }
+
+ int pid;
+ if (!CFNumberGetValue(window_pid, kCFNumberIntType, &pid)) {
+ return 0;
+ }
+
+ return pid;
+}
+
+int GetWindowOwnerPid(CGWindowID id) {
+ int pid;
+ if (GetWindowRef(id, [&pid](CFDictionaryRef window) {
+ pid = GetWindowOwnerPid(window);
+ })) {
+ return pid;
+ }
+ return 0;
+}
+
+float GetScaleFactorAtPosition(const MacDesktopConfiguration& desktop_config,
+ DesktopVector position) {
+ // Find the dpi to physical pixel scale for the screen where the mouse cursor
+ // is.
+ for (auto it = desktop_config.displays.begin();
+ it != desktop_config.displays.end(); ++it) {
+ if (it->bounds.Contains(position)) {
+ return it->dip_to_pixel_scale;
+ }
+ }
+ return 1;
+}
+
+float GetWindowScaleFactor(CGWindowID id, DesktopSize size) {
+ DesktopRect window_bounds = GetWindowBounds(id);
+ float scale = 1.0f;
+
+ if (!window_bounds.is_empty() && !size.is_empty()) {
+ float scale_x = size.width() / window_bounds.width();
+ float scale_y = size.height() / window_bounds.height();
+ // Currently the scale in X and Y directions must be same.
+ if ((std::fabs(scale_x - scale_y) <=
+ std::numeric_limits<float>::epsilon() * std::max(scale_x, scale_y)) &&
+ scale_x > 0.0f) {
+ scale = scale_x;
+ }
+ }
+
+ return scale;
+}
+
+DesktopRect GetWindowBounds(CFDictionaryRef window) {
+ CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
+ CFDictionaryGetValue(window, kCGWindowBounds));
+ if (!window_bounds) {
+ return DesktopRect();
+ }
+
+ CGRect gc_window_rect;
+ if (!CGRectMakeWithDictionaryRepresentation(window_bounds, &gc_window_rect)) {
+ return DesktopRect();
+ }
+
+ return DesktopRect::MakeXYWH(gc_window_rect.origin.x, gc_window_rect.origin.y,
+ gc_window_rect.size.width,
+ gc_window_rect.size.height);
+}
+
+DesktopRect GetWindowBounds(CGWindowID id) {
+ DesktopRect result;
+ if (GetWindowRef(id, [&result](CFDictionaryRef window) {
+ result = GetWindowBounds(window);
+ })) {
+ return result;
+ }
+ return DesktopRect();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h
new file mode 100644
index 0000000000..a9b1e7007c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <string>
+#include "api/function_view.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+
+namespace webrtc {
+
+// Iterates all on-screen windows in decreasing z-order and sends them
+// one-by-one to `on_window` function. If `on_window` returns false, this
+// function returns immediately. GetWindowList() returns false if native APIs
+// failed. Menus, dock (if `only_zero_layer`), minimized windows (if
+// `ignore_minimized` is true) and any windows which do not have a valid window
+// id or title will be ignored.
+bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
+ bool ignore_minimized,
+ bool only_zero_layer);
+
+// Another helper function to get the on-screen windows.
+bool GetWindowList(DesktopCapturer::SourceList* windows,
+ bool ignore_minimized,
+ bool only_zero_layer);
+
+// Returns true if the window is occupying a full screen.
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CFDictionaryRef window);
+
+// Returns true if the window is occupying a full screen.
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CGWindowID id);
+
+// Returns true if the `window` is on screen. This function returns false if
+// native APIs fail.
+bool IsWindowOnScreen(CFDictionaryRef window);
+
+// Returns true if the window is on screen. This function returns false if
+// native APIs fail or `id` cannot be found.
+bool IsWindowOnScreen(CGWindowID id);
+
+// Returns utf-8 encoded title of `window`. If `window` is not a window or no
+// valid title can be retrieved, this function returns an empty string.
+std::string GetWindowTitle(CFDictionaryRef window);
+
+// Returns utf-8 encoded title of window `id`. If `id` cannot be found or no
+// valid title can be retrieved, this function returns an empty string.
+std::string GetWindowTitle(CGWindowID id);
+
+// Returns utf-8 encoded owner name of `window`. If `window` is not a window or
+// if no valid owner name can be retrieved, returns an empty string.
+std::string GetWindowOwnerName(CFDictionaryRef window);
+
+// Returns utf-8 encoded owner name of the given window `id`. If `id` cannot be
+// found or if no valid owner name can be retrieved, returns an empty string.
+std::string GetWindowOwnerName(CGWindowID id);
+
+// Returns id of `window`. If `window` is not a window or the window id cannot
+// be retrieved, this function returns kNullWindowId.
+WindowId GetWindowId(CFDictionaryRef window);
+
+// Returns the pid of the process owning `window`. Return 0 if `window` is not
+// a window or no valid owner can be retrieved.
+int GetWindowOwnerPid(CFDictionaryRef window);
+
+// Returns the pid of the process owning the window `id`. Return 0 if `id`
+// cannot be found or no valid owner can be retrieved.
+int GetWindowOwnerPid(CGWindowID id);
+
+// Returns the DIP to physical pixel scale at `position`. `position` is in
+// *unscaled* system coordinate, i.e. it's device-independent and the primary
+// monitor starts from (0, 0). If `position` is out of the system display, this
+// function returns 1.
+float GetScaleFactorAtPosition(const MacDesktopConfiguration& desktop_config,
+ DesktopVector position);
+
+// Returns the DIP to physical pixel scale factor of the window with `id`.
+// The bounds of the window with `id` is in DIP coordinates and `size` is the
+// CGImage size of the window with `id` in physical coordinates. Comparing them
+// can give the current scale factor.
+// If the window overlaps multiple monitors, OS will decide on which monitor the
+// window is displayed and use its scale factor to the window. So this method
+// still works.
+float GetWindowScaleFactor(CGWindowID id, DesktopSize size);
+
+// Returns the bounds of `window`. If `window` is not a window or the bounds
+// cannot be retrieved, this function returns an empty DesktopRect. The returned
+// DesktopRect is in system coordinate, i.e. the primary monitor always starts
+// from (0, 0).
+// Deprecated: This function should be avoided in favor of the overload with
+// MacDesktopConfiguration.
+DesktopRect GetWindowBounds(CFDictionaryRef window);
+
+// Returns the bounds of window with `id`. If `id` does not represent a window
+// or the bounds cannot be retrieved, this function returns an empty
+// DesktopRect. The returned DesktopRect is in system coordinates.
+// Deprecated: This function should be avoided in favor of the overload with
+// MacDesktopConfiguration.
+DesktopRect GetWindowBounds(CGWindowID id);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_