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:
zijiehe 2017-06-27 22:04:21 -07:00 committed by Commit Bot
parent 8a671751fc
commit 3dd574ad31
6 changed files with 94 additions and 22 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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() {

View File

@ -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

View File

@ -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

View File

@ -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