getDisplayMedia shows black window on Youtube PiP in Windows.

getDisplayMedia capture the view of the screens and windows
in the capture dialog, but the issue is that captured view
of the Youtube somehow is blank. It repros only in certain
circumstances, for example, Canary channel.
If user reinstall the Canary as fresh new, we observed that
it doesn't repro.

Cause:
We aren't sure what's cause of this one yet.

Solution:
We decided to provide fallback WGC capturer when the main
capturer (GDI) shows blank. WGC could show yellow outline
in prior Win11 OS, but yellow outline looks better than
blank.

The blank detector and fallback capturer are what screen capturer
already supported. So, the solution will follow similar
pattern in the window capturer.

Bug: webrtc:13726
Change-Id: I620c817d259d7bb5c295adab11c4444349ab1c6c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/252625
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/main@{#36224}
This commit is contained in:
Sunggook Chue 2022-03-14 11:49:25 -07:00 committed by WebRTC LUCI CQ
parent 0f5b7ebcca
commit 347f9b07b9
6 changed files with 84 additions and 11 deletions

View File

@ -23,8 +23,11 @@ namespace webrtc {
BlankDetectorDesktopCapturerWrapper::BlankDetectorDesktopCapturerWrapper( BlankDetectorDesktopCapturerWrapper::BlankDetectorDesktopCapturerWrapper(
std::unique_ptr<DesktopCapturer> capturer, std::unique_ptr<DesktopCapturer> capturer,
RgbaColor blank_pixel) RgbaColor blank_pixel,
: capturer_(std::move(capturer)), blank_pixel_(blank_pixel) { bool check_per_capture)
: capturer_(std::move(capturer)),
blank_pixel_(blank_pixel),
check_per_capture_(check_per_capture) {
RTC_DCHECK(capturer_); RTC_DCHECK(capturer_);
} }
@ -56,6 +59,13 @@ bool BlankDetectorDesktopCapturerWrapper::GetSourceList(SourceList* sources) {
} }
bool BlankDetectorDesktopCapturerWrapper::SelectSource(SourceId id) { bool BlankDetectorDesktopCapturerWrapper::SelectSource(SourceId id) {
if (check_per_capture_) {
// If we start capturing a new source, we must reset these members
// so we don't short circuit the blank detection logic.
is_first_frame_ = true;
non_blank_frame_received_ = false;
}
return capturer_->SelectSource(id); return capturer_->SelectSource(id);
} }

View File

@ -34,7 +34,8 @@ class BlankDetectorDesktopCapturerWrapper final
// takes ownership of `capturer`. The `blank_pixel` is the unmodified color // takes ownership of `capturer`. The `blank_pixel` is the unmodified color
// returned by the `capturer`. // returned by the `capturer`.
BlankDetectorDesktopCapturerWrapper(std::unique_ptr<DesktopCapturer> capturer, BlankDetectorDesktopCapturerWrapper(std::unique_ptr<DesktopCapturer> capturer,
RgbaColor blank_pixel); RgbaColor blank_pixel,
bool check_per_capture = false);
~BlankDetectorDesktopCapturerWrapper() override; ~BlankDetectorDesktopCapturerWrapper() override;
// DesktopCapturer interface. // DesktopCapturer interface.
@ -70,6 +71,10 @@ class BlankDetectorDesktopCapturerWrapper final
// Whether current frame is the first frame. // Whether current frame is the first frame.
bool is_first_frame_ = true; bool is_first_frame_ = true;
// Blank inspection is made per capture instead of once for all
// screens or windows.
bool check_per_capture_ = false;
DesktopCapturer::Callback* callback_ = nullptr; DesktopCapturer::Callback* callback_ = nullptr;
}; };

View File

@ -163,6 +163,17 @@ class RTC_EXPORT DesktopCaptureOptions {
// precedence over the cropping, directx, and magnification flags. // precedence over the cropping, directx, and magnification flags.
bool allow_wgc_capturer() const { return allow_wgc_capturer_; } bool allow_wgc_capturer() const { return allow_wgc_capturer_; }
void set_allow_wgc_capturer(bool allow) { allow_wgc_capturer_ = allow; } void set_allow_wgc_capturer(bool allow) { allow_wgc_capturer_ = allow; }
// This flag enables the WGC capturer for fallback capturer.
// The flag is useful when the first capturer (eg. WindowCapturerWinGdi) is
// unreliable in certain devices where WGC is supported, but not used by
// default.
bool allow_wgc_capturer_fallback() const {
return allow_wgc_capturer_fallback_;
}
void set_allow_wgc_capturer_fallback(bool allow) {
allow_wgc_capturer_fallback_ = allow;
}
#endif // defined(RTC_ENABLE_WIN_WGC) #endif // defined(RTC_ENABLE_WIN_WGC)
#endif // defined(WEBRTC_WIN) #endif // defined(WEBRTC_WIN)
@ -203,6 +214,7 @@ class RTC_EXPORT DesktopCaptureOptions {
bool allow_cropping_window_capturer_ = false; bool allow_cropping_window_capturer_ = false;
#if defined(RTC_ENABLE_WIN_WGC) #if defined(RTC_ENABLE_WIN_WGC)
bool allow_wgc_capturer_ = false; bool allow_wgc_capturer_ = false;
bool allow_wgc_capturer_fallback_ = false;
#endif #endif
#endif #endif
#if defined(WEBRTC_USE_X11) #if defined(WEBRTC_USE_X11)

View File

@ -47,18 +47,22 @@ void RecordWgcCapturerResult(WgcCapturerResult error) {
WgcCapturerWin::WgcCapturerWin( WgcCapturerWin::WgcCapturerWin(
std::unique_ptr<WgcCaptureSourceFactory> source_factory, std::unique_ptr<WgcCaptureSourceFactory> source_factory,
std::unique_ptr<SourceEnumerator> source_enumerator) std::unique_ptr<SourceEnumerator> source_enumerator,
bool allow_delayed_capturable_check)
: source_factory_(std::move(source_factory)), : source_factory_(std::move(source_factory)),
source_enumerator_(std::move(source_enumerator)) {} source_enumerator_(std::move(source_enumerator)),
allow_delayed_capturable_check_(allow_delayed_capturable_check) {}
WgcCapturerWin::~WgcCapturerWin() = default; WgcCapturerWin::~WgcCapturerWin() = default;
// static // static
std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawWindowCapturer( std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) { const DesktopCaptureOptions& options,
bool allow_delayed_capturable_check) {
return std::make_unique<WgcCapturerWin>( return std::make_unique<WgcCapturerWin>(
std::make_unique<WgcWindowSourceFactory>(), std::make_unique<WgcWindowSourceFactory>(),
std::make_unique<WindowEnumerator>( std::make_unique<WindowEnumerator>(
options.enumerate_current_process_windows())); options.enumerate_current_process_windows()),
allow_delayed_capturable_check);
} }
// static // static
@ -66,7 +70,7 @@ std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawScreenCapturer(
const DesktopCaptureOptions& options) { const DesktopCaptureOptions& options) {
return std::make_unique<WgcCapturerWin>( return std::make_unique<WgcCapturerWin>(
std::make_unique<WgcScreenSourceFactory>(), std::make_unique<WgcScreenSourceFactory>(),
std::make_unique<ScreenEnumerator>()); std::make_unique<ScreenEnumerator>(), false);
} }
bool WgcCapturerWin::GetSourceList(SourceList* sources) { bool WgcCapturerWin::GetSourceList(SourceList* sources) {
@ -75,6 +79,9 @@ bool WgcCapturerWin::GetSourceList(SourceList* sources) {
bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) { bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) {
capture_source_ = source_factory_->CreateCaptureSource(id); capture_source_ = source_factory_->CreateCaptureSource(id);
if (allow_delayed_capturable_check_)
return true;
return capture_source_->IsCapturable(); return capture_source_->IsCapturable();
} }
@ -135,6 +142,13 @@ void WgcCapturerWin::CaptureFrame() {
return; return;
} }
if (allow_delayed_capturable_check_ && !capture_source_->IsCapturable()) {
RTC_LOG(LS_ERROR) << "Source is not capturable.";
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
/*frame=*/nullptr);
return;
}
int64_t capture_start_time_nanos = rtc::TimeNanos(); int64_t capture_start_time_nanos = rtc::TimeNanos();
HRESULT hr; HRESULT hr;

View File

@ -80,7 +80,8 @@ class ScreenEnumerator final : public SourceEnumerator {
class WgcCapturerWin : public DesktopCapturer { class WgcCapturerWin : public DesktopCapturer {
public: public:
WgcCapturerWin(std::unique_ptr<WgcCaptureSourceFactory> source_factory, WgcCapturerWin(std::unique_ptr<WgcCaptureSourceFactory> source_factory,
std::unique_ptr<SourceEnumerator> source_enumerator); std::unique_ptr<SourceEnumerator> source_enumerator,
bool allow_delayed_capturable_check);
WgcCapturerWin(const WgcCapturerWin&) = delete; WgcCapturerWin(const WgcCapturerWin&) = delete;
WgcCapturerWin& operator=(const WgcCapturerWin&) = delete; WgcCapturerWin& operator=(const WgcCapturerWin&) = delete;
@ -88,7 +89,8 @@ class WgcCapturerWin : public DesktopCapturer {
~WgcCapturerWin() override; ~WgcCapturerWin() override;
static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer( static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
const DesktopCaptureOptions& options); const DesktopCaptureOptions& options,
bool allow_delayed_capturable_check = false);
static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer( static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
const DesktopCaptureOptions& options); const DesktopCaptureOptions& options);
@ -128,6 +130,11 @@ class WgcCapturerWin : public DesktopCapturer {
// returns. // returns.
Callback* callback_ = nullptr; Callback* callback_ = nullptr;
// WgcCaptureSource::IsCapturable is expensive to run. So, caller can
// delay capturable check till capture frame is called if the WgcCapturerWin
// is used as a fallback capturer.
bool allow_delayed_capturable_check_ = false;
// A Direct3D11 device that is shared amongst the WgcCaptureSessions, who // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who
// require one to perform the capture. // require one to perform the capture.
Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_; Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_;

View File

@ -12,12 +12,37 @@
#include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/win/window_capturer_win_gdi.h" #include "modules/desktop_capture/win/window_capturer_win_gdi.h"
#if defined(RTC_ENABLE_WIN_WGC)
#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
#include "modules/desktop_capture/win/wgc_capturer_win.h"
#include "rtc_base/win/windows_version.h"
#endif // defined(RTC_ENABLE_WIN_WGC)
namespace webrtc { namespace webrtc {
// static // static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer( std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) { const DesktopCaptureOptions& options) {
return WindowCapturerWinGdi::CreateRawWindowCapturer(options); std::unique_ptr<DesktopCapturer> capturer(
WindowCapturerWinGdi::CreateRawWindowCapturer(options));
#if defined(RTC_ENABLE_WIN_WGC)
if (options.allow_wgc_capturer_fallback() &&
rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_RS5) {
// BlankDectector capturer will send an error when it detects a failed
// GDI rendering, then Fallback capturer will try to capture it again with
// WGC.
capturer = std::make_unique<BlankDetectorDesktopCapturerWrapper>(
std::move(capturer), RgbaColor(0, 0, 0, 0),
/*check_per_capture*/ true);
capturer = std::make_unique<FallbackDesktopCapturerWrapper>(
std::move(capturer),
WgcCapturerWin::CreateRawWindowCapturer(
options, /*allow_delayed_capturable_check*/ true));
}
#endif // defined(RTC_ENABLE_WIN_WGC)
return capturer;
} }
} // namespace webrtc } // namespace webrtc