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 "webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h"
12 
13 #include <comdef.h>
14 #include <DXGI.h>
15 
16 #include <algorithm>
17 
18 #include "webrtc/base/checks.h"
19 #include "webrtc/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.left >= 0 && rect.top >= 0 && rect.right > rect.left &&
29          rect.bottom > rect.top;
30 }
31 
32 }  // namespace
33 
34 DxgiAdapterDuplicator::Context::Context() = default;
35 DxgiAdapterDuplicator::Context::Context(const Context& other) = default;
36 DxgiAdapterDuplicator::Context::~Context() = default;
37 
DxgiAdapterDuplicator(const D3dDevice & device)38 DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device)
39     : device_(device) {}
40 DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default;
41 DxgiAdapterDuplicator::~DxgiAdapterDuplicator() = default;
42 
Initialize()43 bool DxgiAdapterDuplicator::Initialize() {
44   if (DoInitialize()) {
45     return true;
46   }
47   duplicators_.clear();
48   return false;
49 }
50 
DoInitialize()51 bool DxgiAdapterDuplicator::DoInitialize() {
52   for (int i = 0;; i++) {
53     ComPtr<IDXGIOutput> output;
54     _com_error error =
55         device_.dxgi_adapter()->EnumOutputs(i, output.GetAddressOf());
56     if (error.Error() == DXGI_ERROR_NOT_FOUND) {
57       break;
58     }
59 
60     if (error.Error() != S_OK || !output) {
61       LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returns an unexpected "
62                          "result "
63                       << error.ErrorMessage() << " with error code"
64                       << error.Error();
65       return false;
66     }
67 
68     DXGI_OUTPUT_DESC desc;
69     error = output->GetDesc(&desc);
70     if (error.Error() == S_OK) {
71       if (desc.AttachedToDesktop && IsValidRect(desc.DesktopCoordinates)) {
72         ComPtr<IDXGIOutput1> output1;
73         error = output.As(&output1);
74         if (error.Error() != S_OK || !output1) {
75           LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, "
76                              "this usually means the system does not support "
77                              "DirectX 11";
78           return false;
79         }
80         duplicators_.emplace_back(device_, output1, desc);
81         if (!duplicators_.back().Initialize()) {
82           return false;
83         }
84         if (desktop_rect_.is_empty()) {
85           desktop_rect_ = duplicators_.back().desktop_rect();
86         } else {
87           const DesktopRect& left = desktop_rect_;
88           const DesktopRect& right = duplicators_.back().desktop_rect();
89           desktop_rect_ =
90               DesktopRect::MakeLTRB(std::min(left.left(), right.left()),
91                                     std::min(left.top(), right.top()),
92                                     std::max(left.right(), right.right()),
93                                     std::max(left.bottom(), right.bottom()));
94         }
95       }
96     } else {
97       LOG(LS_WARNING) << "Failed to get output description of device " << i
98                       << ", ignore.";
99     }
100   }
101   return true;
102 }
103 
Setup(Context * context)104 void DxgiAdapterDuplicator::Setup(Context* context) {
105   RTC_DCHECK(context->contexts.empty());
106   context->contexts.resize(duplicators_.size());
107   for (size_t i = 0; i < duplicators_.size(); i++) {
108     duplicators_[i].Setup(&context->contexts[i]);
109   }
110 }
111 
Unregister(const Context * const context)112 void DxgiAdapterDuplicator::Unregister(const Context* const context) {
113   RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
114   for (size_t i = 0; i < duplicators_.size(); i++) {
115     duplicators_[i].Unregister(&context->contexts[i]);
116   }
117 }
118 
Duplicate(Context * context,SharedDesktopFrame * target)119 bool DxgiAdapterDuplicator::Duplicate(Context* context,
120                                       SharedDesktopFrame* target) {
121   RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
122   for (size_t i = 0; i < duplicators_.size(); i++) {
123     if (!duplicators_[i].Duplicate(&context->contexts[i],
124                                    duplicators_[i].desktop_rect().top_left(),
125                                    target)) {
126       return false;
127     }
128   }
129   return true;
130 }
131 
DuplicateMonitor(Context * context,int monitor_id,SharedDesktopFrame * target)132 bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context,
133                                              int monitor_id,
134                                              SharedDesktopFrame* target) {
135   RTC_DCHECK(monitor_id >= 0 &&
136              monitor_id < static_cast<int>(duplicators_.size()) &&
137              context->contexts.size() == duplicators_.size());
138   return duplicators_[monitor_id].Duplicate(&context->contexts[monitor_id],
139                                             DesktopVector(), target);
140 }
141 
ScreenRect(int id) const142 DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
143   RTC_DCHECK(id >= 0 && id < static_cast<int>(duplicators_.size()));
144   return duplicators_[id].desktop_rect();
145 }
146 
147 }  // namespace webrtc
148