OLD | NEW |
---|---|
(Empty) | |
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_duplicator_container.h" | |
12 | |
13 #include <windows.h> | |
14 | |
15 #include <algorithm> | |
16 | |
17 #include "webrtc/base/checks.h" | |
18 | |
19 namespace webrtc { | |
20 | |
21 bool DxgiDuplicatorContainer::SingleEntry::Enter() { | |
22 rtc::CritScope lock(&lock_); | |
23 if (entered_) { | |
24 return false; | |
25 } | |
26 entered_ = true; | |
27 return true; | |
28 } | |
29 | |
30 void DxgiDuplicatorContainer::SingleEntry::Exit() { | |
31 rtc::CritScope lock(&lock_); | |
32 RTC_DCHECK(entered_); | |
33 entered_ = false; | |
34 } | |
35 | |
36 bool DxgiDuplicatorContainer::SingleEntry::Hold() const { | |
37 rtc::CritScope lock(&lock_); | |
38 return entered_; | |
39 } | |
40 | |
41 class DxgiDuplicatorContainer::AutoExit { | |
42 public: | |
43 explicit AutoExit(DxgiDuplicatorContainer::SingleEntry* gate) : gate_(gate) { | |
44 RTC_DCHECK(gate_); | |
45 } | |
46 | |
47 ~AutoExit() { gate_->Exit(); } | |
48 | |
49 private: | |
50 DxgiDuplicatorContainer::SingleEntry* gate_; | |
51 }; | |
52 | |
53 // static | |
54 DxgiDuplicatorContainer* DxgiDuplicatorContainer::Instance() { | |
55 // The static instance won't be deleted to ensure it can be used by other | |
56 // threads even during program exiting. | |
57 static DxgiDuplicatorContainer* instance = new DxgiDuplicatorContainer(); | |
58 return instance; | |
59 } | |
60 | |
61 DxgiDuplicatorContainer::DxgiDuplicatorContainer() = default; | |
62 | |
63 DxgiDuplicatorContainer::~DxgiDuplicatorContainer() { | |
64 rtc::CritScope lock(&lock_); | |
65 Deinitialize(); | |
66 } | |
67 | |
68 bool DxgiDuplicatorContainer::Prepare(DxgiContext* context) { | |
69 if (Prepare()) { | |
70 Setup(context); | |
71 return true; | |
72 } | |
73 return false; | |
74 } | |
75 | |
76 bool DxgiDuplicatorContainer::Prepare() { | |
77 // If the instance has been initialized already, does nothing and returns | |
78 // true. | |
79 { | |
80 rtc::CritScope lock(&lock_); | |
81 if (!duplicators_.empty()) { | |
82 return true; | |
83 } | |
84 } | |
85 | |
86 if (initializing_.Enter()) { | |
Sergey Ulanov
2016/07/28 22:15:32
Looking at this again I'm still not sure you reall
Hzj_jie
2016/07/29 02:12:23
Yes, I was wrong, this logic is out-of-date. As we
| |
87 AutoExit exit(&initializing_); | |
88 rtc::CritScope lock(&lock_); | |
89 if (!duplicators_.empty()) { | |
90 // Another thread has initialized this instance before this thread reaches | |
91 // line initializing_.Enter(), so does nothing and returns true. | |
92 return true; | |
93 } | |
94 | |
95 if (DoInitialize()) { | |
96 return true; | |
97 } | |
98 | |
99 Deinitialize(); | |
100 return false; | |
101 } else { | |
102 // Some other thread is initializing, wait for its finish and check the | |
103 // result. | |
104 while (initializing_.Hold()) { | |
105 rtc::CritScope lock(&lock_); | |
106 } | |
107 rtc::CritScope lock(&lock_); | |
108 return !duplicators_.empty(); | |
109 } | |
110 } | |
111 | |
112 bool DxgiDuplicatorContainer::DxgiContextExpired( | |
113 const DxgiContext* const context) const { | |
114 return context->identity_ != identity_ || | |
115 context->contexts_.size() != duplicators_.size(); | |
116 } | |
117 | |
118 void DxgiDuplicatorContainer::Setup(DxgiContext* context) { | |
119 rtc::CritScope lock(&lock_); | |
120 if (duplicators_.empty()) { | |
121 // The most recent initialization has failed. So we do not waste time to | |
122 // setup DxgiContext, it will be setup again later. | |
123 return; | |
124 } | |
125 if (DxgiContextExpired(context)) { | |
126 context->contexts_.clear(); | |
127 context->contexts_.resize(duplicators_.size()); | |
128 for (size_t i = 0; i < duplicators_.size(); i++) { | |
129 duplicators_[i].Setup(&context->contexts_[i]); | |
130 } | |
131 context->identity_ = identity_; | |
132 } | |
133 } | |
134 | |
135 void DxgiDuplicatorContainer::Unregister(const DxgiContext* const context) { | |
136 rtc::CritScope lock(&lock_); | |
137 if (DxgiContextExpired(context)) { | |
138 // The DxgiContext has not been setup after a recent initialization, so it | |
139 // should not been registered in duplicators. | |
140 return; | |
141 } | |
142 for (size_t i = 0; i < duplicators_.size(); i++) { | |
143 duplicators_[i].Unregister(&context->contexts_[i]); | |
144 } | |
145 } | |
146 | |
147 void DxgiDuplicatorContainer::Deinitialize() { | |
148 desktop_rect_ = DesktopRect(); | |
149 duplicators_.clear(); | |
150 } | |
151 | |
152 bool DxgiDuplicatorContainer::DoInitialize() { | |
153 RTC_DCHECK(desktop_rect_.is_empty()); | |
154 RTC_DCHECK(duplicators_.empty()); | |
155 | |
156 std::vector<D3dDevice> devices = D3dDevice::EnumDevices(); | |
157 if (devices.empty()) { | |
158 return false; | |
159 } | |
160 | |
161 for (size_t i = 0; i < devices.size(); i++) { | |
162 duplicators_.emplace_back(devices[i]); | |
Sergey Ulanov
2016/07/28 22:15:32
Please don't use push_back() instead of emplace_ba
Hzj_jie
2016/07/29 02:12:23
Oh, this emplace_back is expected, as devices[i] i
| |
163 if (!duplicators_.back().Initialize()) { | |
164 return false; | |
165 } | |
166 if (desktop_rect_.is_empty()) { | |
167 desktop_rect_ = duplicators_.back().desktop_rect(); | |
168 } else { | |
169 const DesktopRect& left = desktop_rect_; | |
170 const DesktopRect& right = duplicators_.back().desktop_rect(); | |
171 desktop_rect_ = | |
172 DesktopRect::MakeLTRB(std::min(left.left(), right.left()), | |
173 std::min(left.top(), right.top()), | |
174 std::max(left.right(), right.right()), | |
175 std::max(left.bottom(), right.bottom())); | |
176 } | |
177 } | |
178 | |
179 HDC hdc = GetDC(nullptr); | |
180 // Use old DPI value if failed. | |
181 if (hdc != nullptr) { | |
Sergey Ulanov
2016/07/28 22:15:32
nit: s/ != nullptr//
Hzj_jie
2016/07/29 02:12:23
Done.
| |
182 dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); | |
183 ReleaseDC(nullptr, hdc); | |
184 } | |
185 | |
186 identity_++; | |
187 return true; | |
188 } | |
189 | |
190 DesktopRect DxgiDuplicatorContainer::ScreenRect(int id) const { | |
191 RTC_DCHECK(id >= 0); | |
192 rtc::CritScope lock(&lock_); | |
193 for (size_t i = 0; i < duplicators_.size(); i++) { | |
194 if (id >= duplicators_[i].screen_count()) { | |
195 id -= duplicators_[i].screen_count(); | |
196 } else { | |
197 return duplicators_[i].ScreenRect(id); | |
198 } | |
199 } | |
200 return DesktopRect(); | |
201 } | |
202 | |
203 int DxgiDuplicatorContainer::ScreenCount() const { | |
204 rtc::CritScope lock(&lock_); | |
205 int result = 0; | |
206 for (auto& duplicator : duplicators_) { | |
207 result += duplicator.screen_count(); | |
208 } | |
209 return result; | |
210 } | |
211 | |
212 bool DxgiDuplicatorContainer::Duplicate(DxgiContext* context, | |
213 DesktopFrame* target, | |
214 const DesktopFrame* last_frame) { | |
215 RTC_DCHECK(target); | |
216 if (last_frame != nullptr && !target->size().equals(last_frame->size())) { | |
Sergey Ulanov
2016/07/28 22:15:32
nit: s/last_frame != nullptr/last_frame/
Sergey Ulanov
2016/07/28 22:15:32
Replace with a DCHECK()
Hzj_jie
2016/07/29 02:12:23
Done.
Hzj_jie
2016/07/29 02:12:23
Returns false here can help consumers to decide wh
| |
217 return false; | |
218 } | |
219 target->mutable_updated_region()->Clear(); | |
220 rtc::CritScope lock(&lock_); | |
221 if (duplicators_.empty() || DxgiContextExpired(context)) { | |
222 // Next Prepare call will initialize duplicators_ or context. | |
223 return false; | |
224 } | |
225 for (size_t i = 0; i < duplicators_.size(); i++) { | |
226 if (!duplicators_[i].Duplicate(&context->contexts_[i], target, | |
227 last_frame)) { | |
228 Deinitialize(); | |
229 return false; | |
230 } | |
231 } | |
232 target->set_dpi(dpi()); | |
233 return true; | |
234 } | |
235 | |
236 bool DxgiDuplicatorContainer::Duplicate(DxgiContext* context, | |
Sergey Ulanov
2016/07/28 22:15:32
call this DuplicateScreen()?
https://google.github
Hzj_jie
2016/07/29 02:12:23
To match the design doc, changed to DuplicateMonit
| |
237 int id, | |
Sergey Ulanov
2016/07/28 22:15:32
screen_id?
Hzj_jie
2016/07/29 02:12:23
Done.
| |
238 DesktopFrame* target, | |
239 const DesktopFrame* last_frame) { | |
Sergey Ulanov
2016/07/28 22:15:32
swap the last two arguments, see https://google.gi
Hzj_jie
2016/07/29 02:12:23
Order changed, but last_frame may be nullptr.
| |
240 RTC_DCHECK(target); | |
241 RTC_DCHECK(id >= 0); | |
242 if (last_frame != nullptr && !target->size().equals(last_frame->size())) { | |
Sergey Ulanov
2016/07/28 22:15:32
Replace this with a DCHECK.
Hzj_jie
2016/07/29 02:12:23
Same as above.
| |
243 return false; | |
244 } | |
245 target->mutable_updated_region()->Clear(); | |
246 rtc::CritScope lock(&lock_); | |
247 if (duplicators_.empty() || DxgiContextExpired(context)) { | |
248 // Next Prepare call will initialize duplicators_ or context. | |
249 return false; | |
250 } | |
251 for (size_t i = 0; i < duplicators_.size() && i < context->contexts_.size(); | |
252 i++) { | |
253 if (id >= duplicators_[i].screen_count()) { | |
254 id -= duplicators_[i].screen_count(); | |
255 } else { | |
256 if (duplicators_[i].Duplicate(&context->contexts_[i], id, target, | |
257 last_frame)) { | |
258 target->set_dpi(dpi()); | |
259 return true; | |
260 } | |
261 Deinitialize(); | |
262 return false; | |
263 } | |
264 } | |
265 // id >= ScreenCount(). This is a user error, so we do not need to | |
266 // deinitialize. | |
267 return false; | |
268 } | |
269 | |
270 } // namespace webrtc | |
OLD | NEW |