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
16 #include <algorithm>
17
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20
21 namespace webrtc {
22
23 using Microsoft::WRL::ComPtr;
24
25 namespace {
26
IsValidRect(const RECT & rect)27 bool IsValidRect(const RECT& rect) {
28 return rect.right > rect.left && rect.bottom > rect.top;
29 }
30
31 } // namespace
32
DxgiAdapterDuplicator(const D3dDevice & device)33 DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device)
34 : device_(device) {}
35 DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default;
36 DxgiAdapterDuplicator::~DxgiAdapterDuplicator() = default;
37
Initialize()38 bool DxgiAdapterDuplicator::Initialize() {
39 if (DoInitialize()) {
40 return true;
41 }
42 duplicators_.clear();
43 return false;
44 }
45
DoInitialize()46 bool DxgiAdapterDuplicator::DoInitialize() {
47 for (int i = 0;; i++) {
48 ComPtr<IDXGIOutput> output;
49 _com_error error =
50 device_.dxgi_adapter()->EnumOutputs(i, output.GetAddressOf());
51 if (error.Error() == DXGI_ERROR_NOT_FOUND) {
52 break;
53 }
54
55 if (error.Error() == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
56 RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returns "
57 "NOT_CURRENTLY_AVAILABLE. This may happen when "
58 "running in session 0.";
59 break;
60 }
61
62 if (error.Error() != S_OK || !output) {
63 RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returns an unexpected "
64 "result "
65 << error.ErrorMessage() << " with error code"
66 << error.Error();
67 continue;
68 }
69
70 DXGI_OUTPUT_DESC desc;
71 error = output->GetDesc(&desc);
72 if (error.Error() == S_OK) {
73 if (desc.AttachedToDesktop && IsValidRect(desc.DesktopCoordinates)) {
74 ComPtr<IDXGIOutput1> output1;
75 error = output.As(&output1);
76 if (error.Error() != S_OK || !output1) {
77 RTC_LOG(LS_WARNING)
78 << "Failed to convert IDXGIOutput to IDXGIOutput1, "
79 "this usually means the system does not support "
80 "DirectX 11";
81 continue;
82 }
83 DxgiOutputDuplicator duplicator(device_, output1, desc);
84 if (!duplicator.Initialize()) {
85 RTC_LOG(LS_WARNING) << "Failed to initialize DxgiOutputDuplicator on "
86 "output "
87 << i;
88 continue;
89 }
90
91 duplicators_.push_back(std::move(duplicator));
92 desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
93 } else {
94 RTC_LOG(LS_ERROR) << (desc.AttachedToDesktop ? "Attached" : "Detached")
95 << " output " << i << " ("
96 << desc.DesktopCoordinates.top << ", "
97 << desc.DesktopCoordinates.left << ") - ("
98 << desc.DesktopCoordinates.bottom << ", "
99 << desc.DesktopCoordinates.right << ") is ignored.";
100 }
101 } else {
102 RTC_LOG(LS_WARNING) << "Failed to get output description of device " << i
103 << ", ignore.";
104 }
105 }
106
107 if (duplicators_.empty()) {
108 RTC_LOG(LS_WARNING)
109 << "Cannot initialize any DxgiOutputDuplicator instance.";
110 }
111
112 return !duplicators_.empty();
113 }
114
Setup(Context * context)115 void DxgiAdapterDuplicator::Setup(Context* context) {
116 RTC_DCHECK(context->contexts.empty());
117 context->contexts.resize(duplicators_.size());
118 for (size_t i = 0; i < duplicators_.size(); i++) {
119 duplicators_[i].Setup(&context->contexts[i]);
120 }
121 }
122
Unregister(const Context * const context)123 void DxgiAdapterDuplicator::Unregister(const Context* const context) {
124 RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
125 for (size_t i = 0; i < duplicators_.size(); i++) {
126 duplicators_[i].Unregister(&context->contexts[i]);
127 }
128 }
129
Duplicate(Context * context,SharedDesktopFrame * target)130 bool DxgiAdapterDuplicator::Duplicate(Context* context,
131 SharedDesktopFrame* target) {
132 RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
133 for (size_t i = 0; i < duplicators_.size(); i++) {
134 if (!duplicators_[i].Duplicate(&context->contexts[i],
135 duplicators_[i].desktop_rect().top_left(),
136 target)) {
137 return false;
138 }
139 }
140 return true;
141 }
142
DuplicateMonitor(Context * context,int monitor_id,SharedDesktopFrame * target)143 bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context,
144 int monitor_id,
145 SharedDesktopFrame* target) {
146 RTC_DCHECK_GE(monitor_id, 0);
147 RTC_DCHECK_LT(monitor_id, duplicators_.size());
148 RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
149 return duplicators_[monitor_id].Duplicate(&context->contexts[monitor_id],
150 DesktopVector(), target);
151 }
152
ScreenRect(int id) const153 DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
154 RTC_DCHECK_GE(id, 0);
155 RTC_DCHECK_LT(id, duplicators_.size());
156 return duplicators_[id].desktop_rect();
157 }
158
GetDeviceName(int id) const159 const std::string& DxgiAdapterDuplicator::GetDeviceName(int id) const {
160 RTC_DCHECK_GE(id, 0);
161 RTC_DCHECK_LT(id, duplicators_.size());
162 return duplicators_[id].device_name();
163 }
164
screen_count() const165 int DxgiAdapterDuplicator::screen_count() const {
166 return static_cast<int>(duplicators_.size());
167 }
168
GetNumFramesCaptured() const169 int64_t DxgiAdapterDuplicator::GetNumFramesCaptured() const {
170 int64_t min = INT64_MAX;
171 for (const auto& duplicator : duplicators_) {
172 min = std::min(min, duplicator.num_frames_captured());
173 }
174
175 return min;
176 }
177
TranslateRect(const DesktopVector & position)178 void DxgiAdapterDuplicator::TranslateRect(const DesktopVector& position) {
179 desktop_rect_.Translate(position);
180 RTC_DCHECK_GE(desktop_rect_.left(), 0);
181 RTC_DCHECK_GE(desktop_rect_.top(), 0);
182 for (auto& duplicator : duplicators_) {
183 duplicator.TranslateRect(position);
184 }
185 }
186
187 } // namespace webrtc
188