Ensure Dxgi duplicator works correctly in session 0
A recent update of Windows 10 blocks IDXGIAdapter::EnumOutputs() in session 0,
so ScreenCapturerWinDirectx::IsSupported() always returns false in session 0. We
should ensure ScreenCapturerWinDirectx can respond correctly in session 0.
Meanwhile, this change looses the requirement of DirectX capturer: it still
works if some of the video adapters do not support DirectX 11 or
IDXGIOutputDuplication. This issue usually happens when handling a virtual video
adapter.
BUG=webrtc:7809
Review-Url: https://codereview.webrtc.org/2937663003
Cr-Original-Commit-Position: refs/heads/master@{#18797}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 3dd574ad31ce7eaba7d5901ffcd627367bce6b0f
diff --git a/modules/desktop_capture/win/d3d_device.cc b/modules/desktop_capture/win/d3d_device.cc
index 0884314..f1d3c57 100644
--- a/modules/desktop_capture/win/d3d_device.cc
+++ b/modules/desktop_capture/win/d3d_device.cc
@@ -69,6 +69,7 @@
_com_error error = CreateDXGIFactory1(__uuidof(IDXGIFactory1),
reinterpret_cast<void**>(factory.GetAddressOf()));
if (error.Error() != S_OK || !factory) {
+ LOG(LS_WARNING) << "Cannot create IDXGIFactory1.";
return std::vector<D3dDevice>();
}
@@ -78,17 +79,15 @@
error = factory->EnumAdapters(i, adapter.GetAddressOf());
if (error.Error() == S_OK) {
D3dDevice device;
- if (!device.Initialize(adapter)) {
- return std::vector<D3dDevice>();
+ if (device.Initialize(adapter)) {
+ result.push_back(std::move(device));
}
- result.push_back(std::move(device));
} else if (error.Error() == DXGI_ERROR_NOT_FOUND) {
break;
} else {
LOG(LS_WARNING) << "IDXGIFactory1::EnumAdapters returns an unexpected "
"error "
<< error.ErrorMessage() << " with code " << error.Error();
- return std::vector<D3dDevice>();
}
}
return result;
diff --git a/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
index a73f9e3..0d76bd7 100644
--- a/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
+++ b/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
@@ -52,12 +52,19 @@
break;
}
+ if (error.Error() == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
+ LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returns "
+ "NOT_CURRENTLY_AVAILABLE. This may happen when "
+ "running in session 0.";
+ break;
+ }
+
if (error.Error() != S_OK || !output) {
LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returns an unexpected "
"result "
<< error.ErrorMessage() << " with error code"
<< error.Error();
- return false;
+ continue;
}
DXGI_OUTPUT_DESC desc;
@@ -70,12 +77,17 @@
LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, "
"this usually means the system does not support "
"DirectX 11";
- return false;
+ continue;
}
- duplicators_.emplace_back(device_, output1, desc);
- if (!duplicators_.back().Initialize()) {
- return false;
+ DxgiOutputDuplicator duplicator(device_, output1, desc);
+ if (!duplicator.Initialize()) {
+ LOG(LS_WARNING) << "Failed to initialize DxgiOutputDuplicator on "
+ "output "
+ << i;
+ continue;
}
+
+ duplicators_.push_back(std::move(duplicator));
desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
}
} else {
@@ -83,7 +95,12 @@
<< ", ignore.";
}
}
- return true;
+
+ if (duplicators_.empty()) {
+ LOG(LS_WARNING) << "Cannot initialize any DxgiOutputDuplicator instance.";
+ }
+
+ return !duplicators_.empty();
}
void DxgiAdapterDuplicator::Setup(Context* context) {
diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/modules/desktop_capture/win/dxgi_duplicator_controller.cc
index a0bc95f..9ade05b 100644
--- a/modules/desktop_capture/win/dxgi_duplicator_controller.cc
+++ b/modules/desktop_capture/win/dxgi_duplicator_controller.cc
@@ -25,6 +25,22 @@
namespace webrtc {
+namespace {
+
+// TODO(zijiehe): This function should be public as
+// static bool DxgiDuplicatorController::IsSessionUnsupported()
+bool IsRunningInSession0() {
+ DWORD session_id = 0;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) {
+ LOG(LS_WARNING) << "Failed to retrieve current session Id, current binary "
+ "may not have required priviledge.";
+ return true;
+ }
+ return session_id == 0;
+}
+
+} // namespace
+
// static
rtc::scoped_refptr<DxgiDuplicatorController>
DxgiDuplicatorController::Instance() {
@@ -58,12 +74,17 @@
}
bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) {
- rtc::CritScope lock(&lock_);
- if (!Initialize()) {
- return false;
+ bool result = false;
+ {
+ rtc::CritScope lock(&lock_);
+ result = Initialize();
+ *info = d3d_info_;
}
- *info = d3d_info_;
- return true;
+ if (!result) {
+ LOG(LS_WARNING) << "Failed to initialize DXGI components, the D3dInfo "
+ "retrieved may not accurate or out of date.";
+ }
+ return result;
}
DxgiDuplicatorController::Result
@@ -117,6 +138,12 @@
}
if (!Initialize()) {
+ if (succeeded_duplications_ == 0 && IsRunningInSession0()) {
+ LOG(LS_WARNING) << "Current binary is running in session 0. DXGI "
+ "components cannot be initialized.";
+ return Result::UNSUPPORTED_SESSION;
+ }
+
// Cannot initialize COM components now, display mode may be changing.
return Result::INITIALIZATION_FAILED;
}
@@ -128,6 +155,7 @@
frame->frame()->mutable_updated_region()->Clear();
if (DoDuplicateUnlocked(frame->context(), monitor_id, frame->frame())) {
+ succeeded_duplications_++;
return Result::SUCCEEDED;
}
if (monitor_id >= ScreenCountUnlocked()) {
@@ -180,15 +208,11 @@
std::vector<D3dDevice> devices = D3dDevice::EnumDevices();
if (devices.empty()) {
+ LOG(LS_WARNING) << "No D3dDevice found.";
return false;
}
for (size_t i = 0; i < devices.size(); i++) {
- duplicators_.emplace_back(devices[i]);
- if (!duplicators_.back().Initialize()) {
- return false;
- }
-
D3D_FEATURE_LEVEL feature_level =
devices[i].d3d_device()->GetFeatureLevel();
if (d3d_info_.max_feature_level == 0 ||
@@ -200,6 +224,20 @@
d3d_info_.min_feature_level = feature_level;
}
+ DxgiAdapterDuplicator duplicator(devices[i]);
+ // There may be several video cards on the system, some of them may not
+ // support IDXGOutputDuplication. But they should not impact others from
+ // taking effect, so we should continually try other adapters. This usually
+ // happens when a non-official virtual adapter is installed on the system.
+ if (!duplicator.Initialize()) {
+ LOG(LS_WARNING) << "Failed to initialize DxgiAdapterDuplicator on "
+ "adapter "
+ << i;
+ continue;
+ }
+ RTC_DCHECK(!duplicator.desktop_rect().is_empty());
+ duplicators_.push_back(std::move(duplicator));
+
desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
}
TranslateRect();
@@ -212,7 +250,12 @@
}
identity_++;
- return true;
+
+ if (duplicators_.empty()) {
+ LOG(LS_WARNING) << "Cannot initialize any DxgiAdapterDuplicator instance.";
+ }
+
+ return !duplicators_.empty();
}
void DxgiDuplicatorController::Deinitialize() {
diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.h b/modules/desktop_capture/win/dxgi_duplicator_controller.h
index 308b2f3..a254b6d 100644
--- a/modules/desktop_capture/win/dxgi_duplicator_controller.h
+++ b/modules/desktop_capture/win/dxgi_duplicator_controller.h
@@ -60,6 +60,7 @@
enum class Result {
SUCCEEDED,
+ UNSUPPORTED_SESSION,
FRAME_PREPARE_FAILED,
INITIALIZATION_FAILED,
DUPLICATION_FAILED,
@@ -74,7 +75,9 @@
// Detects whether the system supports DXGI based capturer.
bool IsSupported();
- // Returns a copy of D3dInfo composed by last Initialize() function call.
+ // Returns a copy of D3dInfo composed by last Initialize() function call. This
+ // function always copies the latest information into |info|. But once the
+ // function returns false, the information in |info| may not accurate.
bool RetrieveD3dInfo(D3dInfo* info);
// Captures current screen and writes into |frame|.
@@ -211,6 +214,8 @@
std::vector<DxgiAdapterDuplicator> duplicators_;
D3dInfo d3d_info_;
ResolutionChangeDetector resolution_change_detector_;
+ // A number to indicate how many succeeded duplications have been performed.
+ uint32_t succeeded_duplications_ = 0;
};
} // namespace webrtc
diff --git a/modules/desktop_capture/win/dxgi_frame.cc b/modules/desktop_capture/win/dxgi_frame.cc
index 6e33b64..79a9016 100644
--- a/modules/desktop_capture/win/dxgi_frame.cc
+++ b/modules/desktop_capture/win/dxgi_frame.cc
@@ -15,6 +15,7 @@
#include <utility>
#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h"
@@ -46,6 +47,7 @@
frame.reset(new BasicDesktopFrame(size));
}
if (!frame) {
+ LOG(LS_WARNING) << "DxgiFrame cannot create a new DesktopFrame.";
return false;
}
// DirectX capturer won't paint each pixel in the frame due to its one
diff --git a/modules/desktop_capture/win/screen_capturer_win_directx.cc b/modules/desktop_capture/win/screen_capturer_win_directx.cc
index 331379b..f865a1e 100644
--- a/modules/desktop_capture/win/screen_capturer_win_directx.cc
+++ b/modules/desktop_capture/win/screen_capturer_win_directx.cc
@@ -74,6 +74,12 @@
using DuplicateResult = DxgiDuplicatorController::Result;
switch (result) {
+ case DuplicateResult::UNSUPPORTED_SESSION: {
+ LOG(LS_ERROR) << "Current binary is running on a session not supported "
+ "by DirectX screen capturer.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
case DuplicateResult::FRAME_PREPARE_FAILED: {
LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame.";
// This usually means we do not have enough memory or SharedMemoryFactory