Implement handling for MS PowerPoint Presentation Mode.
* Introduce a FullScreenWindowDetector to manage routines for updating the list of sources being application agnostic, inspired by FullScreenChromeWindowDetector. * Introduce a FullScreenApplicationHandler to make a decision about changing window to share in application specific way, inspired by FullScreenChromeWindowDetector. * Remove FullScreenChromeWindowDetector as redundant. * Add FullScreenApplicationHandler for MS PowerPoint and Apple Keynote on MacOS. * Add FullScreenApplicationHandler for MS PowerPoint on Windows. Bug: webrtc:3852 Change-Id: I06507d929308e85b882b2f8210a025afef7f26a9 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/156020 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Reviewed-by: Justin Uberti <juberti@webrtc.org> Reviewed-by: Wez <wez@google.com> Cr-Commit-Position: refs/heads/master@{#29993}
This commit is contained in:
parent
831ce5f171
commit
b588353543
1
AUTHORS
1
AUTHORS
@ -101,6 +101,7 @@ NVIDIA Corporation <*@nvidia.com>
|
||||
Opera Software ASA <*@opera.com>
|
||||
Optical Tone Ltd <*@opticaltone.com>
|
||||
Pengutronix e.K. <*@pengutronix.de>
|
||||
RingCentral, Inc. <*@ringcentral.com>
|
||||
Sinch AB <*@sinch.com>
|
||||
struktur AG <*@struktur.de>
|
||||
Telenor Digital AS <*@telenor.com>
|
||||
|
||||
@ -300,6 +300,10 @@ rtc_library("desktop_capture_generic") {
|
||||
"fake_desktop_capturer.h",
|
||||
"fallback_desktop_capturer_wrapper.cc",
|
||||
"fallback_desktop_capturer_wrapper.h",
|
||||
"full_screen_application_handler.cc",
|
||||
"full_screen_application_handler.h",
|
||||
"full_screen_window_detector.cc",
|
||||
"full_screen_window_detector.h",
|
||||
"mouse_cursor.cc",
|
||||
"mouse_cursor.h",
|
||||
"mouse_cursor_monitor.h",
|
||||
@ -319,8 +323,8 @@ rtc_library("desktop_capture_generic") {
|
||||
"mac/desktop_configuration.h",
|
||||
"mac/desktop_configuration_monitor.cc",
|
||||
"mac/desktop_configuration_monitor.h",
|
||||
"mac/full_screen_chrome_window_detector.cc",
|
||||
"mac/full_screen_chrome_window_detector.h",
|
||||
"mac/full_screen_mac_application_handler.cc",
|
||||
"mac/full_screen_mac_application_handler.h",
|
||||
"mac/window_list_utils.cc",
|
||||
"mac/window_list_utils.h",
|
||||
]
|
||||
@ -431,6 +435,8 @@ rtc_library("desktop_capture_generic") {
|
||||
"win/dxgi_texture_mapping.h",
|
||||
"win/dxgi_texture_staging.cc",
|
||||
"win/dxgi_texture_staging.h",
|
||||
"win/full_screen_win_application_handler.cc",
|
||||
"win/full_screen_win_application_handler.h",
|
||||
"win/scoped_gdi_object.h",
|
||||
"win/scoped_thread_desktop.cc",
|
||||
"win/scoped_thread_desktop.h",
|
||||
@ -470,6 +476,7 @@ rtc_library("desktop_capture_generic") {
|
||||
"../../system_wrappers:cpu_features_api",
|
||||
"../../system_wrappers:metrics",
|
||||
"//third_party/abseil-cpp/absl/memory",
|
||||
"//third_party/abseil-cpp/absl/strings",
|
||||
]
|
||||
|
||||
if (build_with_mozilla) {
|
||||
|
||||
@ -68,6 +68,7 @@ class RTC_EXPORT CroppingWindowCapturer : public DesktopCapturer,
|
||||
|
||||
WindowId selected_window() const { return selected_window_; }
|
||||
WindowId excluded_window() const { return excluded_window_; }
|
||||
DesktopCapturer* window_capturer() const { return window_capturer_.get(); }
|
||||
|
||||
private:
|
||||
DesktopCaptureOptions options_;
|
||||
|
||||
@ -98,26 +98,53 @@ BOOL CALLBACK TopWindowVerifier(HWND hwnd, LPARAM param) {
|
||||
|
||||
class CroppingWindowCapturerWin : public CroppingWindowCapturer {
|
||||
public:
|
||||
CroppingWindowCapturerWin(const DesktopCaptureOptions& options)
|
||||
: CroppingWindowCapturer(options) {}
|
||||
explicit CroppingWindowCapturerWin(const DesktopCaptureOptions& options)
|
||||
: CroppingWindowCapturer(options),
|
||||
full_screen_window_detector_(options.full_screen_window_detector()) {}
|
||||
|
||||
void CaptureFrame() override;
|
||||
|
||||
private:
|
||||
bool ShouldUseScreenCapturer() override;
|
||||
DesktopRect GetWindowRectInVirtualScreen() override;
|
||||
|
||||
// Returns either selected by user sourceId or sourceId provided by
|
||||
// FullScreenWindowDetector
|
||||
WindowId GetWindowToCapture() const;
|
||||
|
||||
// The region from GetWindowRgn in the desktop coordinate if the region is
|
||||
// rectangular, or the rect from GetWindowRect if the region is not set.
|
||||
DesktopRect window_region_rect_;
|
||||
|
||||
WindowCaptureHelperWin window_capture_helper_;
|
||||
|
||||
rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
|
||||
};
|
||||
|
||||
void CroppingWindowCapturerWin::CaptureFrame() {
|
||||
DesktopCapturer* win_capturer = window_capturer();
|
||||
if (win_capturer) {
|
||||
// Update the list of available sources and override source to capture if
|
||||
// FullScreenWindowDetector returns not zero
|
||||
if (full_screen_window_detector_) {
|
||||
full_screen_window_detector_->UpdateWindowListIfNeeded(
|
||||
selected_window(),
|
||||
[win_capturer](DesktopCapturer::SourceList* sources) {
|
||||
return win_capturer->GetSourceList(sources);
|
||||
});
|
||||
}
|
||||
win_capturer->SelectSource(GetWindowToCapture());
|
||||
}
|
||||
|
||||
CroppingWindowCapturer::CaptureFrame();
|
||||
}
|
||||
|
||||
bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() {
|
||||
if (!rtc::IsWindows8OrLater() && window_capture_helper_.IsAeroEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const HWND selected = reinterpret_cast<HWND>(selected_window());
|
||||
const HWND selected = reinterpret_cast<HWND>(GetWindowToCapture());
|
||||
// Check if the window is visible on current desktop.
|
||||
if (!window_capture_helper_.IsWindowVisibleOnCurrentDesktop(selected)) {
|
||||
return false;
|
||||
@ -207,7 +234,7 @@ DesktopRect CroppingWindowCapturerWin::GetWindowRectInVirtualScreen() {
|
||||
TRACE_EVENT0("webrtc",
|
||||
"CroppingWindowCapturerWin::GetWindowRectInVirtualScreen");
|
||||
DesktopRect window_rect;
|
||||
HWND hwnd = reinterpret_cast<HWND>(selected_window());
|
||||
HWND hwnd = reinterpret_cast<HWND>(GetWindowToCapture());
|
||||
if (!GetCroppedWindowRect(hwnd, /*avoid_cropping_border*/ false, &window_rect,
|
||||
/*original_rect*/ nullptr)) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to get window info: " << GetLastError();
|
||||
@ -222,6 +249,15 @@ DesktopRect CroppingWindowCapturerWin::GetWindowRectInVirtualScreen() {
|
||||
return window_rect;
|
||||
}
|
||||
|
||||
WindowId CroppingWindowCapturerWin::GetWindowToCapture() const {
|
||||
const auto selected_source = selected_window();
|
||||
const auto full_screen_source =
|
||||
full_screen_window_detector_
|
||||
? full_screen_window_detector_->FindFullScreenWindow(selected_source)
|
||||
: 0;
|
||||
return full_screen_source ? full_screen_source : selected_source;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
|
||||
@ -9,6 +9,11 @@
|
||||
*/
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
#include "modules/desktop_capture/mac/full_screen_mac_application_handler.h"
|
||||
#elif defined(WEBRTC_WIN)
|
||||
#include "modules/desktop_capture/win/full_screen_win_application_handler.h"
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -32,8 +37,11 @@ DesktopCaptureOptions DesktopCaptureOptions::CreateDefault() {
|
||||
#endif
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
result.set_configuration_monitor(new DesktopConfigurationMonitor());
|
||||
result.set_full_screen_chrome_window_detector(
|
||||
new FullScreenChromeWindowDetector());
|
||||
result.set_full_screen_window_detector(
|
||||
new FullScreenWindowDetector(CreateFullScreenMacApplicationHandler));
|
||||
#elif defined(WEBRTC_WIN)
|
||||
result.set_full_screen_window_detector(
|
||||
new FullScreenWindowDetector(CreateFullScreenWinApplicationHandler));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -19,9 +19,10 @@
|
||||
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
|
||||
#include "modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
|
||||
#endif
|
||||
|
||||
#include "modules/desktop_capture/full_screen_window_detector.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// An object that stores initialization parameters for screen and window
|
||||
@ -62,21 +63,18 @@ class RTC_EXPORT DesktopCaptureOptions {
|
||||
configuration_monitor_ = m;
|
||||
}
|
||||
|
||||
// TODO(zijiehe): Instead of FullScreenChromeWindowDetector, provide a
|
||||
// FullScreenWindowDetector for external consumers to detect the target
|
||||
// fullscreen window.
|
||||
FullScreenChromeWindowDetector* full_screen_chrome_window_detector() const {
|
||||
return full_screen_window_detector_;
|
||||
}
|
||||
void set_full_screen_chrome_window_detector(
|
||||
rtc::scoped_refptr<FullScreenChromeWindowDetector> detector) {
|
||||
full_screen_window_detector_ = detector;
|
||||
}
|
||||
|
||||
bool allow_iosurface() const { return allow_iosurface_; }
|
||||
void set_allow_iosurface(bool allow) { allow_iosurface_ = allow; }
|
||||
#endif
|
||||
|
||||
FullScreenWindowDetector* full_screen_window_detector() const {
|
||||
return full_screen_window_detector_;
|
||||
}
|
||||
void set_full_screen_window_detector(
|
||||
rtc::scoped_refptr<FullScreenWindowDetector> detector) {
|
||||
full_screen_window_detector_ = detector;
|
||||
}
|
||||
|
||||
// Flag indicating that the capturer should use screen change notifications.
|
||||
// Enables/disables use of XDAMAGE in the X11 capturer.
|
||||
bool use_update_notifications() const { return use_update_notifications_; }
|
||||
@ -142,11 +140,11 @@ class RTC_EXPORT DesktopCaptureOptions {
|
||||
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
|
||||
rtc::scoped_refptr<FullScreenChromeWindowDetector>
|
||||
full_screen_window_detector_;
|
||||
bool allow_iosurface_ = false;
|
||||
#endif
|
||||
|
||||
rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
|
||||
|
||||
#if defined(WEBRTC_WIN)
|
||||
bool allow_use_magnification_api_ = false;
|
||||
bool allow_directx_capturer_ = false;
|
||||
|
||||
30
modules/desktop_capture/full_screen_application_handler.cc
Normal file
30
modules/desktop_capture/full_screen_application_handler.cc
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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/full_screen_application_handler.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
FullScreenApplicationHandler::FullScreenApplicationHandler(
|
||||
DesktopCapturer::SourceId sourceId)
|
||||
: source_id_(sourceId) {}
|
||||
|
||||
DesktopCapturer::SourceId FullScreenApplicationHandler::FindFullScreenWindow(
|
||||
const DesktopCapturer::SourceList&,
|
||||
int64_t) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DesktopCapturer::SourceId FullScreenApplicationHandler::GetSourceId() const {
|
||||
return source_id_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
48
modules/desktop_capture/full_screen_application_handler.h
Normal file
48
modules/desktop_capture/full_screen_application_handler.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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_FULL_SCREEN_APPLICATION_HANDLER_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_FULL_SCREEN_APPLICATION_HANDLER_H_
|
||||
|
||||
#include <memory>
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Base class for application specific handler to check criteria for switch to
|
||||
// full-screen mode and find if possible the full-screen window to share.
|
||||
// Supposed to be created and owned by platform specific
|
||||
// FullScreenWindowDetector.
|
||||
class FullScreenApplicationHandler {
|
||||
public:
|
||||
virtual ~FullScreenApplicationHandler() {}
|
||||
|
||||
explicit FullScreenApplicationHandler(DesktopCapturer::SourceId sourceId);
|
||||
|
||||
// Returns the full-screen window in place of the original window if all the
|
||||
// criteria are met, or 0 if no such window found.
|
||||
virtual DesktopCapturer::SourceId FindFullScreenWindow(
|
||||
const DesktopCapturer::SourceList& window_list,
|
||||
int64_t timestamp) const;
|
||||
|
||||
// Returns source id of original window associated with
|
||||
// FullScreenApplicationHandler
|
||||
DesktopCapturer::SourceId GetSourceId() const;
|
||||
|
||||
private:
|
||||
const DesktopCapturer::SourceId source_id_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(FullScreenApplicationHandler);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_FULL_SCREEN_APPLICATION_HANDLER_H_
|
||||
84
modules/desktop_capture/full_screen_window_detector.cc
Normal file
84
modules/desktop_capture/full_screen_window_detector.cc
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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/full_screen_window_detector.h"
|
||||
#include "modules/desktop_capture/full_screen_application_handler.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
FullScreenWindowDetector::FullScreenWindowDetector(
|
||||
ApplicationHandlerFactory application_handler_factory)
|
||||
: application_handler_factory_(application_handler_factory),
|
||||
last_update_time_ms_(0),
|
||||
previous_source_id_(0),
|
||||
no_handler_source_id_(0) {}
|
||||
|
||||
DesktopCapturer::SourceId FullScreenWindowDetector::FindFullScreenWindow(
|
||||
DesktopCapturer::SourceId original_source_id) {
|
||||
if (app_handler_ == nullptr ||
|
||||
app_handler_->GetSourceId() != original_source_id) {
|
||||
return 0;
|
||||
}
|
||||
return app_handler_->FindFullScreenWindow(window_list_, last_update_time_ms_);
|
||||
}
|
||||
|
||||
void FullScreenWindowDetector::UpdateWindowListIfNeeded(
|
||||
DesktopCapturer::SourceId original_source_id,
|
||||
rtc::FunctionView<bool(DesktopCapturer::SourceList*)> get_sources) {
|
||||
const bool skip_update = previous_source_id_ != original_source_id;
|
||||
previous_source_id_ = original_source_id;
|
||||
|
||||
// Here is an attempt to avoid redundant creating application handler in case
|
||||
// when an instance of WindowCapturer is used to generate a thumbnail to show
|
||||
// in picker by calling SelectSource and CaptureFrame for every available
|
||||
// source.
|
||||
if (skip_update) {
|
||||
return;
|
||||
}
|
||||
|
||||
CreateApplicationHandlerIfNeeded(original_source_id);
|
||||
if (app_handler_ == nullptr) {
|
||||
// There is no FullScreenApplicationHandler specific for
|
||||
// current application
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int64_t kUpdateIntervalMs = 500;
|
||||
|
||||
if ((rtc::TimeMillis() - last_update_time_ms_) <= kUpdateIntervalMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
DesktopCapturer::SourceList window_list;
|
||||
if (get_sources(&window_list)) {
|
||||
last_update_time_ms_ = rtc::TimeMillis();
|
||||
window_list_.swap(window_list);
|
||||
}
|
||||
}
|
||||
|
||||
void FullScreenWindowDetector::CreateApplicationHandlerIfNeeded(
|
||||
DesktopCapturer::SourceId source_id) {
|
||||
if (no_handler_source_id_ == source_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (app_handler_ == nullptr || app_handler_->GetSourceId() != source_id) {
|
||||
app_handler_ = application_handler_factory_
|
||||
? application_handler_factory_(source_id)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
if (app_handler_ == nullptr) {
|
||||
no_handler_source_id_ = source_id;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
80
modules/desktop_capture/full_screen_window_detector.h
Normal file
80
modules/desktop_capture/full_screen_window_detector.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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_FULL_SCREEN_WINDOW_DETECTOR_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_FULL_SCREEN_WINDOW_DETECTOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include "api/function_view.h"
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "modules/desktop_capture/full_screen_application_handler.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This is a way to handle switch to full-screen mode for application in some
|
||||
// specific cases:
|
||||
// - Chrome on MacOS creates a new window in full-screen mode to
|
||||
// show a tab full-screen and minimizes the old window.
|
||||
// - PowerPoint creates new windows in full-screen mode when user goes to
|
||||
// presentation mode (Slide Show Window, Presentation Window).
|
||||
//
|
||||
// To continue capturing in these cases, we try to find the new full-screen
|
||||
// window using criteria provided by application specific
|
||||
// FullScreenApplicationHandler.
|
||||
|
||||
class FullScreenWindowDetector : public rtc::RefCountedBase {
|
||||
public:
|
||||
using ApplicationHandlerFactory =
|
||||
std::function<std::unique_ptr<FullScreenApplicationHandler>(
|
||||
DesktopCapturer::SourceId sourceId)>;
|
||||
|
||||
FullScreenWindowDetector(
|
||||
ApplicationHandlerFactory application_handler_factory);
|
||||
|
||||
// Returns the full-screen window in place of the original window if all the
|
||||
// criteria provided by FullScreenApplicationHandler are met, or 0 if no such
|
||||
// window found.
|
||||
DesktopCapturer::SourceId FindFullScreenWindow(
|
||||
DesktopCapturer::SourceId original_source_id);
|
||||
|
||||
// The caller should call this function periodically, implementation will
|
||||
// update internal state no often than twice per second
|
||||
void UpdateWindowListIfNeeded(
|
||||
DesktopCapturer::SourceId original_source_id,
|
||||
rtc::FunctionView<bool(DesktopCapturer::SourceList*)> get_sources);
|
||||
|
||||
static rtc::scoped_refptr<FullScreenWindowDetector>
|
||||
CreateFullScreenWindowDetector();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<FullScreenApplicationHandler> app_handler_;
|
||||
|
||||
private:
|
||||
void CreateApplicationHandlerIfNeeded(DesktopCapturer::SourceId source_id);
|
||||
|
||||
ApplicationHandlerFactory application_handler_factory_;
|
||||
|
||||
int64_t last_update_time_ms_;
|
||||
DesktopCapturer::SourceId previous_source_id_;
|
||||
|
||||
// Save the source id when we fail to create an instance of
|
||||
// CreateApplicationHandlerIfNeeded to avoid redundant attempt to do it again.
|
||||
DesktopCapturer::SourceId no_handler_source_id_;
|
||||
|
||||
DesktopCapturer::SourceList window_list_;
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(FullScreenWindowDetector);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_FULL_SCREEN_WINDOW_DETECTOR_H_
|
||||
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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/full_screen_chrome_window_detector.h"
|
||||
|
||||
#include <libproc.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "modules/desktop_capture/mac/window_list_utils.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t kUpdateIntervalMs = 500;
|
||||
|
||||
// Returns the window that is full-screen and has the same title and owner pid
|
||||
// as the input window.
|
||||
CGWindowID FindFullScreenWindowWithSamePidAndTitle(CGWindowID id) {
|
||||
const int pid = GetWindowOwnerPid(id);
|
||||
std::string title = GetWindowTitle(id);
|
||||
if (title.empty())
|
||||
return kCGNullWindowID;
|
||||
|
||||
// Only get on screen, non-desktop windows.
|
||||
CFArrayRef window_array = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
|
||||
kCGNullWindowID);
|
||||
if (!window_array)
|
||||
return kCGNullWindowID;
|
||||
|
||||
CGWindowID full_screen_window = kCGNullWindowID;
|
||||
|
||||
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));
|
||||
|
||||
CGWindowID window_id = GetWindowId(window);
|
||||
if (window_id == kNullWindowId)
|
||||
continue;
|
||||
|
||||
if (GetWindowOwnerPid(window) != pid)
|
||||
continue;
|
||||
|
||||
std::string window_title = GetWindowTitle(window);
|
||||
if (window_title != title)
|
||||
continue;
|
||||
|
||||
if (IsWindowFullScreen(desktop_config, window)) {
|
||||
full_screen_window = window_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(window_array);
|
||||
return full_screen_window;
|
||||
}
|
||||
|
||||
bool IsChromeWindow(CGWindowID id) {
|
||||
int pid = GetWindowOwnerPid(id);
|
||||
char buffer[PROC_PIDPATHINFO_MAXSIZE];
|
||||
int path_length = proc_pidpath(pid, buffer, sizeof(buffer));
|
||||
if (path_length <= 0)
|
||||
return false;
|
||||
|
||||
const char* last_slash = strrchr(buffer, '/');
|
||||
std::string name(last_slash ? last_slash + 1 : buffer);
|
||||
return name.find("Google Chrome") == 0 || name == "Chromium";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FullScreenChromeWindowDetector::FullScreenChromeWindowDetector()
|
||||
: last_update_time_ns_(0) {}
|
||||
|
||||
FullScreenChromeWindowDetector::~FullScreenChromeWindowDetector() {}
|
||||
|
||||
CGWindowID FullScreenChromeWindowDetector::FindFullScreenWindow(
|
||||
CGWindowID original_window) {
|
||||
if (!IsChromeWindow(original_window) || IsWindowOnScreen(original_window))
|
||||
return kCGNullWindowID;
|
||||
|
||||
CGWindowID full_screen_window_id =
|
||||
FindFullScreenWindowWithSamePidAndTitle(original_window);
|
||||
|
||||
if (full_screen_window_id == kCGNullWindowID)
|
||||
return kCGNullWindowID;
|
||||
|
||||
for (const auto& window : previous_window_list_) {
|
||||
if (static_cast<CGWindowID>(window.id) != full_screen_window_id)
|
||||
continue;
|
||||
|
||||
RTC_LOG(LS_WARNING) << "The full-screen window exists in the list.";
|
||||
return kCGNullWindowID;
|
||||
}
|
||||
|
||||
return full_screen_window_id;
|
||||
}
|
||||
|
||||
void FullScreenChromeWindowDetector::UpdateWindowListIfNeeded(
|
||||
CGWindowID original_window) {
|
||||
if (IsChromeWindow(original_window) &&
|
||||
(rtc::TimeNanos() - last_update_time_ns_) / rtc::kNumNanosecsPerMillisec >
|
||||
kUpdateIntervalMs) {
|
||||
previous_window_list_.clear();
|
||||
previous_window_list_.swap(current_window_list_);
|
||||
|
||||
// No need to update the window list when the window is minimized.
|
||||
if (!IsWindowOnScreen(original_window)) {
|
||||
previous_window_list_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
GetWindowList(¤t_window_list_, false);
|
||||
last_update_time_ns_ = rtc::TimeNanos();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This is a work around for the Chrome tab full-screen behavior: Chrome
|
||||
// creates a new window in full-screen mode to show a tab full-screen and
|
||||
// minimizes the old window. To continue capturing in this case, we try to
|
||||
// find the new full-screen window using these criteria:
|
||||
// 0. The original shared window is minimized.
|
||||
// 1. The original shared window's owner application name is "Google Chrome".
|
||||
// 2. The original window and the new window have the same title and owner
|
||||
// pid.
|
||||
// 3. The new window is full-screen.
|
||||
// 4. The new window didn't exist at least 500 millisecond ago.
|
||||
|
||||
class FullScreenChromeWindowDetector : public rtc::RefCountedBase {
|
||||
public:
|
||||
FullScreenChromeWindowDetector();
|
||||
|
||||
// Returns the full-screen window in place of the original window if all the
|
||||
// criteria are met, or kCGNullWindowID if no such window found.
|
||||
CGWindowID FindFullScreenWindow(CGWindowID original_window);
|
||||
|
||||
// The caller should call this function periodically, no less than twice per
|
||||
// second.
|
||||
void UpdateWindowListIfNeeded(CGWindowID original_window);
|
||||
|
||||
protected:
|
||||
~FullScreenChromeWindowDetector() override;
|
||||
|
||||
private:
|
||||
// We cache the last two results of the window list, so
|
||||
// |previous_window_list_| is taken at least 500ms before the next Capture()
|
||||
// call. If we only save the last result, we may get false positive (i.e.
|
||||
// full-screen window exists in the list) if Capture() is called too soon.
|
||||
DesktopCapturer::SourceList current_window_list_;
|
||||
DesktopCapturer::SourceList previous_window_list_;
|
||||
int64_t last_update_time_ns_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(FullScreenChromeWindowDetector);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_
|
||||
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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 "modules/desktop_capture/mac/window_list_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
static constexpr const char* kPowerPointSlideShowTitles[] = {
|
||||
u8"PowerPoint-Bildschirmpräsentation",
|
||||
u8"Προβολή παρουσίασης PowerPoint",
|
||||
u8"PowerPoint スライド ショー",
|
||||
u8"PowerPoint Slide Show",
|
||||
u8"PowerPoint 幻灯片放映",
|
||||
u8"Presentación de PowerPoint",
|
||||
u8"PowerPoint-slideshow",
|
||||
u8"Presentazione di PowerPoint",
|
||||
u8"Prezentácia programu PowerPoint",
|
||||
u8"Apresentação do PowerPoint",
|
||||
u8"PowerPoint-bildspel",
|
||||
u8"Prezentace v aplikaci PowerPoint",
|
||||
u8"PowerPoint 슬라이드 쇼",
|
||||
u8"PowerPoint-lysbildefremvisning",
|
||||
u8"PowerPoint-vetítés",
|
||||
u8"PowerPoint Slayt Gösterisi",
|
||||
u8"Pokaz slajdów programu PowerPoint",
|
||||
u8"PowerPoint 投影片放映",
|
||||
u8"Демонстрация PowerPoint",
|
||||
u8"Diaporama PowerPoint",
|
||||
u8"PowerPoint-diaesitys",
|
||||
u8"Peragaan Slide PowerPoint",
|
||||
u8"PowerPoint-diavoorstelling",
|
||||
u8"การนำเสนอสไลด์ PowerPoint",
|
||||
u8"Apresentação de slides do PowerPoint",
|
||||
u8"הצגת שקופיות של PowerPoint",
|
||||
u8"عرض شرائح في PowerPoint"};
|
||||
|
||||
class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
|
||||
public:
|
||||
using TitlePredicate =
|
||||
std::function<bool(const std::string&, const std::string&)>;
|
||||
|
||||
FullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId,
|
||||
TitlePredicate title_predicate)
|
||||
: FullScreenApplicationHandler(sourceId),
|
||||
title_predicate_(title_predicate),
|
||||
owner_pid_(GetWindowOwnerPid(sourceId)) {}
|
||||
|
||||
void InvalidateCacheIfNeeded(const DesktopCapturer::SourceList& source_list,
|
||||
int64_t timestamp) const {
|
||||
// Copy only sources with the same pid
|
||||
if (timestamp != cache_timestamp_) {
|
||||
cache_sources_.clear();
|
||||
std::copy_if(source_list.begin(), source_list.end(),
|
||||
std::back_inserter(cache_sources_),
|
||||
[&](const DesktopCapturer::Source& src) {
|
||||
return src.id != GetSourceId() &&
|
||||
GetWindowOwnerPid(src.id) == owner_pid_;
|
||||
});
|
||||
cache_timestamp_ = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
WindowId FindFullScreenWindowWithSamePid(
|
||||
const DesktopCapturer::SourceList& source_list,
|
||||
int64_t timestamp) const {
|
||||
InvalidateCacheIfNeeded(source_list, timestamp);
|
||||
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 IsWindowOnScreen(GetSourceId())
|
||||
? 0
|
||||
: FindFullScreenWindowWithSamePid(source_list, timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
const TitlePredicate title_predicate_;
|
||||
const int owner_pid_;
|
||||
mutable int64_t cache_timestamp_ = 0;
|
||||
mutable DesktopCapturer::SourceList cache_sources_;
|
||||
};
|
||||
|
||||
bool equal_title_predicate(const std::string& original_title,
|
||||
const std::string& title) {
|
||||
return original_title == title;
|
||||
}
|
||||
|
||||
bool slide_show_title_predicate(const std::string& original_title,
|
||||
const std::string& title) {
|
||||
if (title.find(original_title) == std::string::npos)
|
||||
return false;
|
||||
|
||||
for (const char* pp_slide_title : kPowerPointSlideShowTitles) {
|
||||
if (absl::StartsWith(title, pp_slide_title))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // 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};
|
||||
FullScreenMacApplicationHandler::TitlePredicate predicate = nullptr;
|
||||
if (name.find("Google Chrome") == 0 || name == "Chromium") {
|
||||
predicate = equal_title_predicate;
|
||||
} else if (name == "Microsoft PowerPoint") {
|
||||
predicate = slide_show_title_predicate;
|
||||
} else if (name == "Keynote") {
|
||||
predicate = equal_title_predicate;
|
||||
}
|
||||
|
||||
if (predicate) {
|
||||
result.reset(new FullScreenMacApplicationHandler(sourceId, predicate));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -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_
|
||||
@ -15,8 +15,11 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
@ -78,7 +81,8 @@ bool GetWindowRef(CGWindowID id,
|
||||
} // namespace
|
||||
|
||||
bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
|
||||
bool ignore_minimized) {
|
||||
bool ignore_minimized,
|
||||
bool only_zero_layer) {
|
||||
RTC_DCHECK(on_window);
|
||||
|
||||
// Only get on screen, non-desktop windows.
|
||||
@ -122,7 +126,7 @@ bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
|
||||
if (!CFNumberGetValue(window_layer, kCFNumberIntType, &layer)) {
|
||||
continue;
|
||||
}
|
||||
if (layer != 0) {
|
||||
if (only_zero_layer && layer != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -151,7 +155,8 @@ bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
|
||||
}
|
||||
|
||||
bool GetWindowList(DesktopCapturer::SourceList* windows,
|
||||
bool ignore_minimized) {
|
||||
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;
|
||||
@ -201,7 +206,7 @@ bool GetWindowList(DesktopCapturer::SourceList* windows,
|
||||
}
|
||||
return true;
|
||||
},
|
||||
ignore_minimized);
|
||||
ignore_minimized, only_zero_layer);
|
||||
|
||||
if (!ret)
|
||||
return false;
|
||||
@ -239,6 +244,15 @@ bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
|
||||
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));
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
#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"
|
||||
@ -24,18 +25,26 @@ 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, minimized windows (if |ignore_minimized| is true) and
|
||||
// any windows which do not have a valid window id or title will be ignored.
|
||||
// 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 ignore_minimized,
|
||||
bool only_zero_layer);
|
||||
|
||||
// Another helper function to get the on-screen windows.
|
||||
bool GetWindowList(DesktopCapturer::SourceList* windows, bool ignore_minimized);
|
||||
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);
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
#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/full_screen_chrome_window_detector.h"
|
||||
#include "modules/desktop_capture/mac/window_list_utils.h"
|
||||
#include "modules/desktop_capture/mouse_cursor.h"
|
||||
|
||||
@ -79,21 +78,16 @@ class MouseCursorMonitorMac : public MouseCursorMonitor {
|
||||
Callback* callback_;
|
||||
Mode mode_;
|
||||
__strong NSImage* last_cursor_;
|
||||
rtc::scoped_refptr<FullScreenChromeWindowDetector>
|
||||
full_screen_chrome_window_detector_;
|
||||
};
|
||||
|
||||
MouseCursorMonitorMac::MouseCursorMonitorMac(
|
||||
const DesktopCaptureOptions& options,
|
||||
CGWindowID window_id,
|
||||
ScreenId screen_id)
|
||||
MouseCursorMonitorMac::MouseCursorMonitorMac(const DesktopCaptureOptions& options,
|
||||
CGWindowID window_id,
|
||||
ScreenId screen_id)
|
||||
: configuration_monitor_(options.configuration_monitor()),
|
||||
window_id_(window_id),
|
||||
screen_id_(screen_id),
|
||||
callback_(NULL),
|
||||
mode_(SHAPE_AND_POSITION),
|
||||
full_screen_chrome_window_detector_(
|
||||
options.full_screen_chrome_window_detector()) {
|
||||
mode_(SHAPE_AND_POSITION) {
|
||||
assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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/win/full_screen_win_application_handler.h"
|
||||
#include <algorithm>
|
||||
#include <cwctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/logging.h" // For RTC_LOG_GLE
|
||||
#include "rtc_base/string_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
std::string WindowText(HWND window) {
|
||||
size_t len = ::GetWindowTextLength(window);
|
||||
if (len == 0)
|
||||
return std::string();
|
||||
|
||||
std::vector<wchar_t> buffer(len + 1, 0);
|
||||
size_t copied = ::GetWindowTextW(window, buffer.data(), buffer.size());
|
||||
if (copied == 0)
|
||||
return std::string();
|
||||
return rtc::ToUtf8(buffer.data(), copied);
|
||||
}
|
||||
|
||||
DWORD WindowProcessId(HWND window) {
|
||||
DWORD dwProcessId = 0;
|
||||
::GetWindowThreadProcessId(window, &dwProcessId);
|
||||
return dwProcessId;
|
||||
}
|
||||
|
||||
std::wstring FileNameFromPath(const std::wstring& path) {
|
||||
auto found = path.rfind(L"\\");
|
||||
if (found == std::string::npos)
|
||||
return path;
|
||||
return path.substr(found + 1);
|
||||
}
|
||||
|
||||
// Returns windows which belong to given process id
|
||||
// |sources| is a full list of available windows
|
||||
// |processId| is a process identifier (window owner)
|
||||
// |window_to_exclude| is a window to be exluded from result
|
||||
DesktopCapturer::SourceList GetProcessWindows(
|
||||
const DesktopCapturer::SourceList& sources,
|
||||
DWORD processId,
|
||||
HWND window_to_exclude) {
|
||||
DesktopCapturer::SourceList result;
|
||||
std::copy_if(sources.begin(), sources.end(), std::back_inserter(result),
|
||||
[&](DesktopCapturer::Source source) {
|
||||
const HWND source_hwnd = reinterpret_cast<HWND>(source.id);
|
||||
return window_to_exclude != source_hwnd &&
|
||||
WindowProcessId(source_hwnd) == processId;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
class FullScreenPowerPointHandler : public FullScreenApplicationHandler {
|
||||
public:
|
||||
explicit FullScreenPowerPointHandler(DesktopCapturer::SourceId sourceId)
|
||||
: FullScreenApplicationHandler(sourceId) {}
|
||||
|
||||
~FullScreenPowerPointHandler() override {}
|
||||
|
||||
DesktopCapturer::SourceId FindFullScreenWindow(
|
||||
const DesktopCapturer::SourceList& window_list,
|
||||
int64_t timestamp) const override {
|
||||
if (window_list.empty())
|
||||
return 0;
|
||||
|
||||
HWND original_window = reinterpret_cast<HWND>(GetSourceId());
|
||||
DWORD process_id = WindowProcessId(original_window);
|
||||
|
||||
DesktopCapturer::SourceList powerpoint_windows =
|
||||
GetProcessWindows(window_list, process_id, original_window);
|
||||
|
||||
if (powerpoint_windows.empty())
|
||||
return 0;
|
||||
|
||||
if (GetWindowType(original_window) != WindowType::kEditor)
|
||||
return 0;
|
||||
|
||||
const auto original_document = GetDocumentFromEditorTitle(original_window);
|
||||
|
||||
for (const auto& source : powerpoint_windows) {
|
||||
HWND window = reinterpret_cast<HWND>(source.id);
|
||||
|
||||
// Looking for slide show window for the same document
|
||||
if (GetWindowType(window) != WindowType::kSlideShow ||
|
||||
GetDocumentFromSlideShowTitle(window) != original_document) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return source.id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class WindowType { kEditor, kSlideShow, kOther };
|
||||
|
||||
WindowType GetWindowType(HWND window) const {
|
||||
if (IsEditorWindow(window))
|
||||
return WindowType::kEditor;
|
||||
else if (IsSlideShowWindow(window))
|
||||
return WindowType::kSlideShow;
|
||||
else
|
||||
return WindowType::kOther;
|
||||
}
|
||||
|
||||
constexpr static char kDocumentTitleSeparator[] = " - ";
|
||||
|
||||
std::string GetDocumentFromEditorTitle(HWND window) const {
|
||||
std::string title = WindowText(window);
|
||||
auto position = title.find(kDocumentTitleSeparator);
|
||||
return rtc::string_trim(title.substr(0, position));
|
||||
}
|
||||
|
||||
std::string GetDocumentFromSlideShowTitle(HWND window) const {
|
||||
std::string title = WindowText(window);
|
||||
auto left_pos = title.find(kDocumentTitleSeparator);
|
||||
auto right_pos = title.rfind(kDocumentTitleSeparator);
|
||||
constexpr size_t kSeparatorLength = arraysize(kDocumentTitleSeparator) - 1;
|
||||
if (left_pos == std::string::npos || right_pos == std::string::npos)
|
||||
return title;
|
||||
|
||||
if (right_pos > left_pos + kSeparatorLength) {
|
||||
auto result_len = right_pos - left_pos - kSeparatorLength;
|
||||
auto document = title.substr(left_pos + kSeparatorLength, result_len);
|
||||
return rtc::string_trim(document);
|
||||
} else {
|
||||
auto document =
|
||||
title.substr(left_pos + kSeparatorLength, std::wstring::npos);
|
||||
return rtc::string_trim(document);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEditorWindow(HWND window) const {
|
||||
constexpr WCHAR kScreenClassName[] = L"PPTFrameClass";
|
||||
constexpr size_t kScreenClassNameLength = arraysize(kScreenClassName) - 1;
|
||||
|
||||
// We need to verify that window class is equal to |kScreenClassName|.
|
||||
// To do that we need a buffer large enough to include a null terminated
|
||||
// string one code point bigger than |kScreenClassName|. It will help us to
|
||||
// check that size of class name string returned by GetClassNameW is equal
|
||||
// to |kScreenClassNameLength| not being limited by size of buffer (case
|
||||
// when |kScreenClassName| is a prefix for class name string).
|
||||
WCHAR buffer[arraysize(kScreenClassName) + 3];
|
||||
const int length = ::GetClassNameW(window, buffer, arraysize(buffer));
|
||||
if (length != kScreenClassNameLength)
|
||||
return false;
|
||||
return wcsncmp(buffer, kScreenClassName, kScreenClassNameLength) == 0;
|
||||
}
|
||||
|
||||
bool IsSlideShowWindow(HWND window) const {
|
||||
const LONG style = ::GetWindowLong(window, GWL_STYLE);
|
||||
const bool min_box = WS_MINIMIZEBOX & style;
|
||||
const bool max_box = WS_MAXIMIZEBOX & style;
|
||||
return !min_box && !max_box;
|
||||
}
|
||||
};
|
||||
|
||||
std::wstring GetPathByWindowId(HWND window_id) {
|
||||
DWORD process_id = WindowProcessId(window_id);
|
||||
HANDLE process =
|
||||
::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
|
||||
if (process == NULL)
|
||||
return L"";
|
||||
DWORD path_len = MAX_PATH;
|
||||
WCHAR path[MAX_PATH];
|
||||
std::wstring result;
|
||||
if (::QueryFullProcessImageNameW(process, 0, path, &path_len))
|
||||
result = std::wstring(path, path_len);
|
||||
else
|
||||
RTC_LOG_GLE(LS_ERROR) << "QueryFullProcessImageName failed.";
|
||||
|
||||
::CloseHandle(process);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<FullScreenApplicationHandler>
|
||||
CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId source_id) {
|
||||
std::unique_ptr<FullScreenApplicationHandler> result;
|
||||
std::wstring exe_path = GetPathByWindowId(reinterpret_cast<HWND>(source_id));
|
||||
std::wstring file_name = FileNameFromPath(exe_path);
|
||||
std::transform(file_name.begin(), file_name.end(), file_name.begin(),
|
||||
std::towupper);
|
||||
|
||||
if (file_name == L"POWERPNT.EXE") {
|
||||
result = std::make_unique<FullScreenPowerPointHandler>(source_id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -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_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
|
||||
#define MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
|
||||
|
||||
#include <memory>
|
||||
#include "modules/desktop_capture/full_screen_application_handler.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::unique_ptr<FullScreenApplicationHandler>
|
||||
CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId sourceId);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
|
||||
@ -22,7 +22,6 @@
|
||||
#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/full_screen_chrome_window_detector.h"
|
||||
#include "modules/desktop_capture/mac/window_list_utils.h"
|
||||
#include "modules/desktop_capture/window_finder_mac.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
@ -48,10 +47,9 @@ bool IsWindowValid(CGWindowID id) {
|
||||
|
||||
class WindowCapturerMac : public DesktopCapturer {
|
||||
public:
|
||||
explicit WindowCapturerMac(rtc::scoped_refptr<FullScreenChromeWindowDetector>
|
||||
full_screen_chrome_window_detector,
|
||||
rtc::scoped_refptr<DesktopConfigurationMonitor>
|
||||
configuration_monitor);
|
||||
explicit WindowCapturerMac(
|
||||
rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
|
||||
rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor);
|
||||
~WindowCapturerMac() override;
|
||||
|
||||
// DesktopCapturer interface.
|
||||
@ -68,8 +66,7 @@ class WindowCapturerMac : public DesktopCapturer {
|
||||
// The window being captured.
|
||||
CGWindowID window_id_ = 0;
|
||||
|
||||
const rtc::scoped_refptr<FullScreenChromeWindowDetector>
|
||||
full_screen_chrome_window_detector_;
|
||||
rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
|
||||
|
||||
const rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
|
||||
|
||||
@ -79,18 +76,16 @@ class WindowCapturerMac : public DesktopCapturer {
|
||||
};
|
||||
|
||||
WindowCapturerMac::WindowCapturerMac(
|
||||
rtc::scoped_refptr<FullScreenChromeWindowDetector>
|
||||
full_screen_chrome_window_detector,
|
||||
rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
|
||||
rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor)
|
||||
: full_screen_chrome_window_detector_(
|
||||
std::move(full_screen_chrome_window_detector)),
|
||||
: 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);
|
||||
return webrtc::GetWindowList(sources, true, true);
|
||||
}
|
||||
|
||||
bool WindowCapturerMac::SelectSource(SourceId id) {
|
||||
@ -163,12 +158,15 @@ void WindowCapturerMac::CaptureFrame() {
|
||||
}
|
||||
|
||||
CGWindowID on_screen_window = window_id_;
|
||||
if (full_screen_chrome_window_detector_) {
|
||||
CGWindowID full_screen_window =
|
||||
full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_);
|
||||
if (full_screen_window_detector_) {
|
||||
full_screen_window_detector_->UpdateWindowListIfNeeded(
|
||||
window_id_, [](DesktopCapturer::SourceList* sources) {
|
||||
return webrtc::GetWindowList(sources, true, false);
|
||||
});
|
||||
|
||||
if (full_screen_window != kCGNullWindowID)
|
||||
on_screen_window = full_screen_window;
|
||||
CGWindowID full_screen_window = full_screen_window_detector_->FindFullScreenWindow(window_id_);
|
||||
|
||||
if (full_screen_window != kCGNullWindowID) on_screen_window = full_screen_window;
|
||||
}
|
||||
|
||||
std::unique_ptr<DesktopFrame> frame = DesktopFrameCGImage::CreateForWindow(on_screen_window);
|
||||
@ -186,9 +184,6 @@ void WindowCapturerMac::CaptureFrame() {
|
||||
frame->set_dpi(DesktopVector(kStandardDPI * scale_factor, kStandardDPI * scale_factor));
|
||||
|
||||
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
|
||||
|
||||
if (full_screen_chrome_window_detector_)
|
||||
full_screen_chrome_window_detector_->UpdateWindowListIfNeeded(window_id_);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -196,9 +191,8 @@ void WindowCapturerMac::CaptureFrame() {
|
||||
// static
|
||||
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
|
||||
const DesktopCaptureOptions& options) {
|
||||
return std::unique_ptr<DesktopCapturer>(
|
||||
new WindowCapturerMac(options.full_screen_chrome_window_detector(),
|
||||
options.configuration_monitor()));
|
||||
return std::unique_ptr<DesktopCapturer>(new WindowCapturerMac(
|
||||
options.full_screen_window_detector(), options.configuration_monitor()));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -38,6 +38,7 @@ WindowId WindowFinderMac::GetWindowUnderPoint(DesktopVector point) {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true,
|
||||
true);
|
||||
return id;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user