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