OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h" | 11 #include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h" |
12 | 12 |
13 #include <windows.h> | 13 #include <windows.h> |
14 | 14 |
15 #include <algorithm> | 15 #include <algorithm> |
16 #include <string> | 16 #include <string> |
17 | 17 |
18 #include "webrtc/base/checks.h" | 18 #include "webrtc/base/checks.h" |
19 #include "webrtc/base/timeutils.h" | 19 #include "webrtc/base/timeutils.h" |
20 #include "webrtc/modules/desktop_capture/desktop_capture_types.h" | 20 #include "webrtc/modules/desktop_capture/desktop_capture_types.h" |
| 21 #include "webrtc/modules/desktop_capture/win/dxgi_frame.h" |
21 #include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" | 22 #include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" |
22 #include "webrtc/system_wrappers/include/sleep.h" | 23 #include "webrtc/system_wrappers/include/sleep.h" |
23 | 24 |
24 namespace webrtc { | 25 namespace webrtc { |
25 | 26 |
26 DxgiDuplicatorController::Context::Context() = default; | |
27 | |
28 DxgiDuplicatorController::Context::~Context() { | |
29 DxgiDuplicatorController::Instance()->Unregister(this); | |
30 } | |
31 | |
32 void DxgiDuplicatorController::Context::Reset() { | |
33 identity_ = 0; | |
34 } | |
35 | |
36 // static | 27 // static |
37 DxgiDuplicatorController* DxgiDuplicatorController::Instance() { | 28 DxgiDuplicatorController* DxgiDuplicatorController::Instance() { |
38 // The static instance won't be deleted to ensure it can be used by other | 29 // The static instance won't be deleted to ensure it can be used by other |
39 // threads even during program exiting. | 30 // threads even during program exiting. |
40 static DxgiDuplicatorController* instance = new DxgiDuplicatorController(); | 31 static DxgiDuplicatorController* instance = new DxgiDuplicatorController(); |
41 return instance; | 32 return instance; |
42 } | 33 } |
43 | 34 |
44 DxgiDuplicatorController::DxgiDuplicatorController() = default; | 35 DxgiDuplicatorController::DxgiDuplicatorController() = default; |
45 | 36 |
46 DxgiDuplicatorController::~DxgiDuplicatorController() { | 37 DxgiDuplicatorController::~DxgiDuplicatorController() { |
47 rtc::CritScope lock(&lock_); | 38 rtc::CritScope lock(&lock_); |
48 Deinitialize(); | 39 Deinitialize(); |
49 } | 40 } |
50 | 41 |
51 bool DxgiDuplicatorController::IsSupported() { | 42 bool DxgiDuplicatorController::IsSupported() { |
52 rtc::CritScope lock(&lock_); | 43 rtc::CritScope lock(&lock_); |
53 return Initialize(); | 44 return Initialize(); |
54 } | 45 } |
55 | 46 |
56 void DxgiDuplicatorController::Reset() { | |
57 rtc::CritScope lock(&lock_); | |
58 Deinitialize(); | |
59 } | |
60 | |
61 bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) { | 47 bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) { |
62 rtc::CritScope lock(&lock_); | 48 rtc::CritScope lock(&lock_); |
63 if (!Initialize()) { | 49 if (!Initialize()) { |
64 return false; | 50 return false; |
65 } | 51 } |
66 *info = d3d_info_; | 52 *info = d3d_info_; |
67 return true; | 53 return true; |
68 } | 54 } |
69 | 55 |
| 56 DxgiDuplicatorController::Result |
| 57 DxgiDuplicatorController::Duplicate(DxgiFrame* frame) { |
| 58 return DoDuplicate(frame, -1); |
| 59 } |
| 60 |
| 61 DxgiDuplicatorController::Result |
| 62 DxgiDuplicatorController::DuplicateMonitor(DxgiFrame* frame, int monitor_id) { |
| 63 RTC_DCHECK_GE(monitor_id, 0); |
| 64 return DoDuplicate(frame, monitor_id); |
| 65 } |
| 66 |
70 DesktopVector DxgiDuplicatorController::dpi() { | 67 DesktopVector DxgiDuplicatorController::dpi() { |
71 rtc::CritScope lock(&lock_); | 68 rtc::CritScope lock(&lock_); |
72 if (Initialize()) { | 69 if (Initialize()) { |
73 return dpi_; | 70 return dpi_; |
74 } | 71 } |
75 return DesktopVector(); | 72 return DesktopVector(); |
76 } | 73 } |
77 | 74 |
78 DesktopRect DxgiDuplicatorController::desktop_rect() { | |
79 rtc::CritScope lock(&lock_); | |
80 if (Initialize()) { | |
81 return desktop_rect_; | |
82 } | |
83 return DesktopRect(); | |
84 } | |
85 | |
86 DesktopSize DxgiDuplicatorController::desktop_size() { | |
87 DesktopRect rect = desktop_rect(); | |
88 return DesktopSize(rect.right(), rect.bottom()); | |
89 } | |
90 | |
91 DesktopRect DxgiDuplicatorController::ScreenRect(int id) { | |
92 RTC_DCHECK(id >= 0); | |
93 rtc::CritScope lock(&lock_); | |
94 if (!Initialize()) { | |
95 return DesktopRect(); | |
96 } | |
97 for (size_t i = 0; i < duplicators_.size(); i++) { | |
98 if (id >= duplicators_[i].screen_count()) { | |
99 id -= duplicators_[i].screen_count(); | |
100 } else { | |
101 return duplicators_[i].ScreenRect(id); | |
102 } | |
103 } | |
104 return DesktopRect(); | |
105 } | |
106 | |
107 int DxgiDuplicatorController::ScreenCount() { | 75 int DxgiDuplicatorController::ScreenCount() { |
108 rtc::CritScope lock(&lock_); | 76 rtc::CritScope lock(&lock_); |
109 return ScreenCountUnlocked(); | 77 if (Initialize()) { |
| 78 return ScreenCountUnlocked(); |
| 79 } |
| 80 return 0; |
| 81 } |
| 82 |
| 83 DxgiDuplicatorController::Result |
| 84 DxgiDuplicatorController::DoDuplicate(DxgiFrame* frame, int monitor_id) { |
| 85 RTC_DCHECK(frame); |
| 86 rtc::CritScope lock(&lock_); |
| 87 |
| 88 // The dxgi components and APIs do not update the screen resolution without |
| 89 // a reinitialization. So we use the GetDC() function to retrieve the screen |
| 90 // resolution to decide whether dxgi components need to be reinitialized. |
| 91 // If the screen resolution changed, it's very likely the next Duplicate() |
| 92 // function call will fail because of a missing monitor or the frame size is |
| 93 // not enough to store the output. So we reinitialize dxgi components in-place |
| 94 // to avoid a capture failure. |
| 95 // But there is no guarantee GetDC() function returns the same resolution as |
| 96 // dxgi APIs, we still rely on dxgi components to return the output frame |
| 97 // size. |
| 98 // TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and |
| 99 // IDXGIOutputDuplication::GetDesc() can detect the resolution change without |
| 100 // reinitialization. |
| 101 if (resolution_change_detector_.IsChanged( |
| 102 GetScreenRect(kFullDesktopScreenId, std::wstring()).size())) { |
| 103 Deinitialize(); |
| 104 } |
| 105 |
| 106 if (!Initialize()) { |
| 107 // Cannot initialize COM components now, display mode may be changing. |
| 108 return Result::INITIALIZATION_FAILED; |
| 109 } |
| 110 |
| 111 if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) { |
| 112 return Result::FRAME_PREPARE_FAILED; |
| 113 } |
| 114 |
| 115 frame->frame()->mutable_updated_region()->Clear(); |
| 116 |
| 117 if (DoDuplicateUnlocked(frame->context(), monitor_id, frame->frame())) { |
| 118 return Result::SUCCEEDED; |
| 119 } |
| 120 if (monitor_id >= ScreenCountUnlocked()) { |
| 121 // It's a user error to provide a |monitor_id| larger than screen count. We |
| 122 // do not need to deinitialize. |
| 123 return Result::INVALID_MONITOR_ID; |
| 124 } |
| 125 |
| 126 // If the |monitor_id| is valid, but DoDuplicateUnlocked() failed, something |
| 127 // must be wrong from capturer APIs. We should Deinitialize(). |
| 128 Deinitialize(); |
| 129 return Result::DUPLICATION_FAILED; |
110 } | 130 } |
111 | 131 |
112 void DxgiDuplicatorController::Unregister(const Context* const context) { | 132 void DxgiDuplicatorController::Unregister(const Context* const context) { |
113 rtc::CritScope lock(&lock_); | 133 rtc::CritScope lock(&lock_); |
114 if (ContextExpired(context)) { | 134 if (ContextExpired(context)) { |
115 // The Context has not been setup after a recent initialization, so it | 135 // The Context has not been setup after a recent initialization, so it |
116 // should not been registered in duplicators. | 136 // should not been registered in duplicators. |
117 return; | 137 return; |
118 } | 138 } |
119 for (size_t i = 0; i < duplicators_.size(); i++) { | 139 for (size_t i = 0; i < duplicators_.size(); i++) { |
120 duplicators_[i].Unregister(&context->contexts_[i]); | 140 duplicators_[i].Unregister(&context->contexts[i]); |
121 } | 141 } |
122 } | 142 } |
123 | 143 |
124 bool DxgiDuplicatorController::Initialize() { | 144 bool DxgiDuplicatorController::Initialize() { |
125 if (!duplicators_.empty()) { | 145 if (!duplicators_.empty()) { |
126 return true; | 146 return true; |
127 } | 147 } |
128 | 148 |
129 if (DoInitialize()) { | 149 if (DoInitialize()) { |
130 return true; | 150 return true; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 } | 207 } |
188 | 208 |
189 void DxgiDuplicatorController::Deinitialize() { | 209 void DxgiDuplicatorController::Deinitialize() { |
190 desktop_rect_ = DesktopRect(); | 210 desktop_rect_ = DesktopRect(); |
191 duplicators_.clear(); | 211 duplicators_.clear(); |
192 resolution_change_detector_.Reset(); | 212 resolution_change_detector_.Reset(); |
193 } | 213 } |
194 | 214 |
195 bool DxgiDuplicatorController::ContextExpired( | 215 bool DxgiDuplicatorController::ContextExpired( |
196 const Context* const context) const { | 216 const Context* const context) const { |
197 return context->identity_ != identity_ || | 217 RTC_DCHECK(context); |
198 context->contexts_.size() != duplicators_.size(); | 218 return context->controller_id != identity_ || |
| 219 context->contexts.size() != duplicators_.size(); |
199 } | 220 } |
200 | 221 |
201 void DxgiDuplicatorController::Setup(Context* context) { | 222 void DxgiDuplicatorController::Setup(Context* context) { |
202 if (ContextExpired(context)) { | 223 if (ContextExpired(context)) { |
203 context->contexts_.clear(); | 224 RTC_DCHECK(context); |
204 context->contexts_.resize(duplicators_.size()); | 225 context->contexts.clear(); |
| 226 context->contexts.resize(duplicators_.size()); |
205 for (size_t i = 0; i < duplicators_.size(); i++) { | 227 for (size_t i = 0; i < duplicators_.size(); i++) { |
206 duplicators_[i].Setup(&context->contexts_[i]); | 228 duplicators_[i].Setup(&context->contexts[i]); |
207 } | 229 } |
208 context->identity_ = identity_; | 230 context->controller_id = identity_; |
209 } | 231 } |
210 } | 232 } |
211 | 233 |
212 bool DxgiDuplicatorController::Duplicate(Context* context, | |
213 SharedDesktopFrame* target) { | |
214 return DoDuplicate(context, -1, target); | |
215 } | |
216 | |
217 bool DxgiDuplicatorController::DuplicateMonitor(Context* context, | |
218 int monitor_id, | |
219 SharedDesktopFrame* target) { | |
220 RTC_DCHECK_GE(monitor_id, 0); | |
221 return DoDuplicate(context, monitor_id, target); | |
222 } | |
223 | |
224 bool DxgiDuplicatorController::DoDuplicate(Context* context, | |
225 int monitor_id, | |
226 SharedDesktopFrame* target) { | |
227 RTC_DCHECK(target); | |
228 target->mutable_updated_region()->Clear(); | |
229 rtc::CritScope lock(&lock_); | |
230 if (DoDuplicateUnlocked(context, monitor_id, target)) { | |
231 return true; | |
232 } | |
233 if (monitor_id < ScreenCountUnlocked()) { | |
234 // It's a user error to provide a |monitor_id| larger than screen count. We | |
235 // do not need to deinitialize. | |
236 Deinitialize(); | |
237 } | |
238 return false; | |
239 } | |
240 | |
241 bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, | 234 bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, |
242 int monitor_id, | 235 int monitor_id, |
243 SharedDesktopFrame* target) { | 236 SharedDesktopFrame* target) { |
244 if (!Initialize()) { | |
245 // Cannot initialize COM components now, display mode may be changing. | |
246 return false; | |
247 } | |
248 | |
249 if (resolution_change_detector_.IsChanged( | |
250 GetScreenRect(kFullDesktopScreenId, std::wstring()).size())) { | |
251 // Resolution of entire screen has been changed, which usually means a new | |
252 // monitor has been attached or one has been removed. The simplest way is to | |
253 // Deinitialize() and returns false to indicate downstream components. | |
254 return false; | |
255 } | |
256 | |
257 Setup(context); | 237 Setup(context); |
258 | 238 |
259 if (!EnsureFrameCaptured(context, target)) { | 239 if (!EnsureFrameCaptured(context, target)) { |
260 return false; | 240 return false; |
261 } | 241 } |
262 | 242 |
263 bool result = false; | 243 bool result = false; |
264 if (monitor_id < 0) { | 244 if (monitor_id < 0) { |
265 // Capture entire screen. | 245 // Capture entire screen. |
266 result = DoDuplicateAll(context, target); | 246 result = DoDuplicateAll(context, target); |
267 } else { | 247 } else { |
268 result = DoDuplicateOne(context, monitor_id, target); | 248 result = DoDuplicateOne(context, monitor_id, target); |
269 } | 249 } |
270 | 250 |
271 if (result) { | 251 if (result) { |
272 target->set_dpi(dpi()); | 252 target->set_dpi(dpi()); |
273 return true; | 253 return true; |
274 } | 254 } |
275 | 255 |
276 return false; | 256 return false; |
277 } | 257 } |
278 | 258 |
279 bool DxgiDuplicatorController::DoDuplicateAll(Context* context, | 259 bool DxgiDuplicatorController::DoDuplicateAll(Context* context, |
280 SharedDesktopFrame* target) { | 260 SharedDesktopFrame* target) { |
281 for (size_t i = 0; i < duplicators_.size(); i++) { | 261 for (size_t i = 0; i < duplicators_.size(); i++) { |
282 if (!duplicators_[i].Duplicate(&context->contexts_[i], target)) { | 262 if (!duplicators_[i].Duplicate(&context->contexts[i], target)) { |
283 return false; | 263 return false; |
284 } | 264 } |
285 } | 265 } |
286 return true; | 266 return true; |
287 } | 267 } |
288 | 268 |
289 bool DxgiDuplicatorController::DoDuplicateOne(Context* context, | 269 bool DxgiDuplicatorController::DoDuplicateOne(Context* context, |
290 int monitor_id, | 270 int monitor_id, |
291 SharedDesktopFrame* target) { | 271 SharedDesktopFrame* target) { |
292 RTC_DCHECK(monitor_id >= 0); | 272 RTC_DCHECK(monitor_id >= 0); |
293 for (size_t i = 0; i < duplicators_.size() && i < context->contexts_.size(); | 273 for (size_t i = 0; i < duplicators_.size() && i < context->contexts.size(); |
294 i++) { | 274 i++) { |
295 if (monitor_id >= duplicators_[i].screen_count()) { | 275 if (monitor_id >= duplicators_[i].screen_count()) { |
296 monitor_id -= duplicators_[i].screen_count(); | 276 monitor_id -= duplicators_[i].screen_count(); |
297 } else { | 277 } else { |
298 if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id, | 278 if (duplicators_[i].DuplicateMonitor(&context->contexts[i], monitor_id, |
299 target)) { | 279 target)) { |
300 return true; | 280 return true; |
301 } | 281 } |
302 return false; | 282 return false; |
303 } | 283 } |
304 } | 284 } |
305 return false; | 285 return false; |
306 } | 286 } |
307 | 287 |
308 int64_t DxgiDuplicatorController::GetNumFramesCaptured() const { | 288 int64_t DxgiDuplicatorController::GetNumFramesCaptured() const { |
309 int64_t min = INT64_MAX; | 289 int64_t min = INT64_MAX; |
310 for (const auto& duplicator : duplicators_) { | 290 for (const auto& duplicator : duplicators_) { |
311 min = std::min(min, duplicator.GetNumFramesCaptured()); | 291 min = std::min(min, duplicator.GetNumFramesCaptured()); |
312 } | 292 } |
313 | 293 |
314 return min; | 294 return min; |
315 } | 295 } |
316 | 296 |
317 int DxgiDuplicatorController::ScreenCountUnlocked() { | 297 DesktopSize DxgiDuplicatorController::desktop_size() const { |
318 if (!Initialize()) { | 298 return DesktopSize(desktop_rect_.right(), desktop_rect_.bottom()); |
319 return 0; | 299 } |
| 300 |
| 301 DesktopRect DxgiDuplicatorController::ScreenRect(int id) const { |
| 302 RTC_DCHECK(id >= 0); |
| 303 for (size_t i = 0; i < duplicators_.size(); i++) { |
| 304 if (id >= duplicators_[i].screen_count()) { |
| 305 id -= duplicators_[i].screen_count(); |
| 306 } else { |
| 307 return duplicators_[i].ScreenRect(id); |
| 308 } |
320 } | 309 } |
| 310 return DesktopRect(); |
| 311 } |
| 312 |
| 313 int DxgiDuplicatorController::ScreenCountUnlocked() const { |
321 int result = 0; | 314 int result = 0; |
322 for (auto& duplicator : duplicators_) { | 315 for (auto& duplicator : duplicators_) { |
323 result += duplicator.screen_count(); | 316 result += duplicator.screen_count(); |
324 } | 317 } |
325 return result; | 318 return result; |
326 } | 319 } |
327 | 320 |
| 321 DesktopSize DxgiDuplicatorController::SelectedDesktopSize( |
| 322 int monitor_id) const { |
| 323 if (monitor_id < 0) { |
| 324 return desktop_size(); |
| 325 } |
| 326 |
| 327 return ScreenRect(monitor_id).size(); |
| 328 } |
| 329 |
328 bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, | 330 bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, |
329 SharedDesktopFrame* target) { | 331 SharedDesktopFrame* target) { |
330 // On a modern system, the FPS / monitor refresh rate is usually larger than | 332 // On a modern system, the FPS / monitor refresh rate is usually larger than |
331 // or equal to 60. So 17 milliseconds is enough to capture at least one frame. | 333 // or equal to 60. So 17 milliseconds is enough to capture at least one frame. |
332 const int64_t ms_per_frame = 17; | 334 const int64_t ms_per_frame = 17; |
333 // Skips the first frame to ensure a full frame refresh has happened before | 335 // Skips the first frame to ensure a full frame refresh has happened before |
334 // this function returns. | 336 // this function returns. |
335 const int64_t frames_to_skip = 1; | 337 const int64_t frames_to_skip = 1; |
336 // The total time out milliseconds for this function. If we cannot get enough | 338 // The total time out milliseconds for this function. If we cannot get enough |
337 // frames during this time interval, this function returns false, and cause | 339 // frames during this time interval, this function returns false, and cause |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 return false; | 372 return false; |
371 } | 373 } |
372 if (rtc::TimeMillis() - start_ms > timeout_ms) { | 374 if (rtc::TimeMillis() - start_ms > timeout_ms) { |
373 return false; | 375 return false; |
374 } | 376 } |
375 } | 377 } |
376 return true; | 378 return true; |
377 } | 379 } |
378 | 380 |
379 } // namespace webrtc | 381 } // namespace webrtc |
OLD | NEW |