dxgi_adapter_duplicator.cc (6893B)
1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/desktop_capture/win/dxgi_adapter_duplicator.h" 12 13 #include <comdef.h> 14 #include <dxgi.h> 15 #include <wrl/client.h> 16 17 #include <algorithm> 18 #include <cstddef> 19 #include <cstdint> 20 #include <optional> 21 #include <string> 22 #include <utility> 23 24 #include "modules/desktop_capture/desktop_geometry.h" 25 #include "modules/desktop_capture/shared_desktop_frame.h" 26 #include "modules/desktop_capture/win/d3d_device.h" 27 #include "modules/desktop_capture/win/desktop_capture_utils.h" 28 #include "modules/desktop_capture/win/dxgi_output_duplicator.h" 29 #include "rtc_base/checks.h" 30 #include "rtc_base/logging.h" 31 32 namespace webrtc { 33 34 using Microsoft::WRL::ComPtr; 35 36 namespace { 37 38 bool IsValidRect(const RECT& rect) { 39 return rect.right > rect.left && rect.bottom > rect.top; 40 } 41 42 } // namespace 43 44 DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device) 45 : device_(device) {} 46 DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default; 47 DxgiAdapterDuplicator::~DxgiAdapterDuplicator() = default; 48 49 bool DxgiAdapterDuplicator::Initialize() { 50 if (DoInitialize()) { 51 return true; 52 } 53 duplicators_.clear(); 54 return false; 55 } 56 57 bool DxgiAdapterDuplicator::DoInitialize() { 58 for (int i = 0;; i++) { 59 ComPtr<IDXGIOutput> output; 60 _com_error error = 61 device_.dxgi_adapter()->EnumOutputs(i, output.GetAddressOf()); 62 if (error.Error() == DXGI_ERROR_NOT_FOUND) { 63 break; 64 } 65 66 if (error.Error() == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { 67 RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returned " 68 << "NOT_CURRENTLY_AVAILABLE. This may happen when " 69 << "running in session 0."; 70 break; 71 } 72 73 if (error.Error() != S_OK || !output) { 74 RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returned an unexpected " 75 << "result: " 76 << desktop_capture::utils::ComErrorToString(error); 77 continue; 78 } 79 80 DXGI_OUTPUT_DESC desc; 81 error = output->GetDesc(&desc); 82 if (error.Error() == S_OK) { 83 if (desc.AttachedToDesktop && IsValidRect(desc.DesktopCoordinates)) { 84 ComPtr<IDXGIOutput1> output1; 85 error = output.As(&output1); 86 if (error.Error() != S_OK || !output1) { 87 RTC_LOG(LS_WARNING) 88 << "Failed to convert IDXGIOutput to IDXGIOutput1, this usually " 89 << "means the system does not support DirectX 11"; 90 continue; 91 } 92 DxgiOutputDuplicator duplicator(device_, output1, desc); 93 if (!duplicator.Initialize()) { 94 RTC_LOG(LS_WARNING) << "Failed to initialize DxgiOutputDuplicator on " 95 << "output " << i; 96 continue; 97 } 98 99 duplicators_.push_back(std::move(duplicator)); 100 desktop_rect_.UnionWith(duplicators_.back().desktop_rect()); 101 } else { 102 RTC_LOG(LS_ERROR) << (desc.AttachedToDesktop ? "Attached" : "Detached") 103 << " output " << i << " (" 104 << desc.DesktopCoordinates.top << ", " 105 << desc.DesktopCoordinates.left << ") - (" 106 << desc.DesktopCoordinates.bottom << ", " 107 << desc.DesktopCoordinates.right << ") is ignored."; 108 } 109 } else { 110 RTC_LOG(LS_WARNING) << "Failed to get output description of device " << i 111 << ", ignore."; 112 } 113 } 114 115 if (duplicators_.empty()) { 116 RTC_LOG(LS_WARNING) 117 << "Cannot initialize any DxgiOutputDuplicator instance."; 118 } 119 120 return !duplicators_.empty(); 121 } 122 123 void DxgiAdapterDuplicator::Setup(Context* context) { 124 RTC_DCHECK(context->contexts.empty()); 125 context->contexts.resize(duplicators_.size()); 126 for (size_t i = 0; i < duplicators_.size(); i++) { 127 duplicators_[i].Setup(&context->contexts[i]); 128 } 129 } 130 131 void DxgiAdapterDuplicator::Unregister(const Context* const context) { 132 RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size()); 133 for (size_t i = 0; i < duplicators_.size(); i++) { 134 duplicators_[i].Unregister(&context->contexts[i]); 135 } 136 } 137 138 bool DxgiAdapterDuplicator::Duplicate(Context* context, 139 SharedDesktopFrame* target) { 140 RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size()); 141 for (size_t i = 0; i < duplicators_.size(); i++) { 142 if (!duplicators_[i].Duplicate(&context->contexts[i], 143 duplicators_[i].desktop_rect().top_left(), 144 target)) { 145 return false; 146 } 147 } 148 return true; 149 } 150 151 bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context, 152 int monitor_id, 153 SharedDesktopFrame* target) { 154 RTC_DCHECK_GE(monitor_id, 0); 155 RTC_DCHECK_LT(monitor_id, duplicators_.size()); 156 RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size()); 157 return duplicators_[monitor_id].Duplicate(&context->contexts[monitor_id], 158 DesktopVector(), target); 159 } 160 161 std::optional<float> DxgiAdapterDuplicator::GetDeviceScaleFactor( 162 int screen_id) const { 163 if (screen_id < 0 || static_cast<size_t>(screen_id) >= duplicators_.size()) { 164 return std::nullopt; 165 } 166 return duplicators_[screen_id].device_scale_factor(); 167 } 168 169 DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const { 170 RTC_DCHECK_GE(id, 0); 171 RTC_DCHECK_LT(id, duplicators_.size()); 172 return duplicators_[id].desktop_rect(); 173 } 174 175 const std::string& DxgiAdapterDuplicator::GetDeviceName(int id) const { 176 RTC_DCHECK_GE(id, 0); 177 RTC_DCHECK_LT(id, duplicators_.size()); 178 return duplicators_[id].device_name(); 179 } 180 181 int DxgiAdapterDuplicator::screen_count() const { 182 return static_cast<int>(duplicators_.size()); 183 } 184 185 int64_t DxgiAdapterDuplicator::GetNumFramesCaptured(int monitor_id) const { 186 int64_t min = INT64_MAX; 187 if (monitor_id < 0) { 188 for (const auto& duplicator : duplicators_) { 189 min = std::min(min, duplicator.num_frames_captured()); 190 } 191 } else if (static_cast<size_t>(monitor_id) < duplicators_.size()) { 192 min = duplicators_[monitor_id].num_frames_captured(); 193 } 194 return min; 195 } 196 197 void DxgiAdapterDuplicator::TranslateRect(const DesktopVector& position) { 198 desktop_rect_.Translate(position); 199 RTC_DCHECK_GE(desktop_rect_.left(), 0); 200 RTC_DCHECK_GE(desktop_rect_.top(), 0); 201 for (auto& duplicator : duplicators_) { 202 duplicator.TranslateRect(position); 203 } 204 } 205 206 } // namespace webrtc