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-Commit-Position: refs/heads/master@{#18797}
This commit is contained in:
parent
8a671751fc
commit
3dd574ad31
@ -69,6 +69,7 @@ std::vector<D3dDevice> D3dDevice::EnumDevices() {
|
||||
_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 @@ std::vector<D3dDevice> D3dDevice::EnumDevices() {
|
||||
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;
|
||||
|
||||
@ -52,12 +52,19 @@ bool DxgiAdapterDuplicator::DoInitialize() {
|
||||
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 @@ bool DxgiAdapterDuplicator::DoInitialize() {
|
||||
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 @@ bool DxgiAdapterDuplicator::DoInitialize() {
|
||||
<< ", ignore.";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
if (duplicators_.empty()) {
|
||||
LOG(LS_WARNING) << "Cannot initialize any DxgiOutputDuplicator instance.";
|
||||
}
|
||||
|
||||
return !duplicators_.empty();
|
||||
}
|
||||
|
||||
void DxgiAdapterDuplicator::Setup(Context* context) {
|
||||
|
||||
@ -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::IsSupported() {
|
||||
}
|
||||
|
||||
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 @@ DxgiDuplicatorController::DoDuplicate(DxgiFrame* frame, int monitor_id) {
|
||||
}
|
||||
|
||||
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 @@ DxgiDuplicatorController::DoDuplicate(DxgiFrame* frame, int monitor_id) {
|
||||
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 @@ bool DxgiDuplicatorController::DoInitialize() {
|
||||
|
||||
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 @@ bool DxgiDuplicatorController::DoInitialize() {
|
||||
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 @@ bool DxgiDuplicatorController::DoInitialize() {
|
||||
}
|
||||
|
||||
identity_++;
|
||||
return true;
|
||||
|
||||
if (duplicators_.empty()) {
|
||||
LOG(LS_WARNING) << "Cannot initialize any DxgiAdapterDuplicator instance.";
|
||||
}
|
||||
|
||||
return !duplicators_.empty();
|
||||
}
|
||||
|
||||
void DxgiDuplicatorController::Deinitialize() {
|
||||
|
||||
@ -60,6 +60,7 @@ class DxgiDuplicatorController {
|
||||
|
||||
enum class Result {
|
||||
SUCCEEDED,
|
||||
UNSUPPORTED_SESSION,
|
||||
FRAME_PREPARE_FAILED,
|
||||
INITIALIZATION_FAILED,
|
||||
DUPLICATION_FAILED,
|
||||
@ -74,7 +75,9 @@ class DxgiDuplicatorController {
|
||||
// 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 @@ class DxgiDuplicatorController {
|
||||
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
|
||||
|
||||
@ -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 @@ bool DxgiFrame::Prepare(DesktopSize size, DesktopCapturer::SourceId source_id) {
|
||||
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
|
||||
|
||||
@ -74,6 +74,12 @@ void ScreenCapturerWinDirectx::CaptureFrame() {
|
||||
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user