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