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 DxgiDuplicatorController::Context::Context() = default; |
27 | 28 |
28 DxgiDuplicatorController::Context::~Context() { | 29 DxgiDuplicatorController::Context::~Context() { |
29 DxgiDuplicatorController::Instance()->Unregister(this); | 30 DxgiDuplicatorController::Instance()->Unregister(this); |
30 } | 31 } |
(...skipping 15 matching lines...) Expand all Loading... |
46 DxgiDuplicatorController::~DxgiDuplicatorController() { | 47 DxgiDuplicatorController::~DxgiDuplicatorController() { |
47 rtc::CritScope lock(&lock_); | 48 rtc::CritScope lock(&lock_); |
48 Deinitialize(); | 49 Deinitialize(); |
49 } | 50 } |
50 | 51 |
51 bool DxgiDuplicatorController::IsSupported() { | 52 bool DxgiDuplicatorController::IsSupported() { |
52 rtc::CritScope lock(&lock_); | 53 rtc::CritScope lock(&lock_); |
53 return Initialize(); | 54 return Initialize(); |
54 } | 55 } |
55 | 56 |
56 void DxgiDuplicatorController::Reset() { | |
57 rtc::CritScope lock(&lock_); | |
58 Deinitialize(); | |
59 } | |
60 | |
61 bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) { | 57 bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) { |
62 rtc::CritScope lock(&lock_); | 58 rtc::CritScope lock(&lock_); |
63 if (!Initialize()) { | 59 if (!Initialize()) { |
64 return false; | 60 return false; |
65 } | 61 } |
66 *info = d3d_info_; | 62 *info = d3d_info_; |
67 return true; | 63 return true; |
68 } | 64 } |
69 | 65 |
| 66 DxgiDuplicatorController::Result |
| 67 DxgiDuplicatorController::Duplicate(DxgiFrame* frame) { |
| 68 return DoDuplicate(frame, -1); |
| 69 } |
| 70 |
| 71 DxgiDuplicatorController::Result |
| 72 DxgiDuplicatorController::DuplicateMonitor(DxgiFrame* frame, int monitor_id) { |
| 73 RTC_DCHECK_GE(monitor_id, 0); |
| 74 return DoDuplicate(frame, monitor_id); |
| 75 } |
| 76 |
70 DesktopVector DxgiDuplicatorController::dpi() { | 77 DesktopVector DxgiDuplicatorController::dpi() { |
71 rtc::CritScope lock(&lock_); | 78 rtc::CritScope lock(&lock_); |
72 if (Initialize()) { | 79 if (Initialize()) { |
73 return dpi_; | 80 return dpi_; |
74 } | 81 } |
75 return DesktopVector(); | 82 return DesktopVector(); |
76 } | 83 } |
77 | 84 |
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() { | 85 int DxgiDuplicatorController::ScreenCount() { |
108 rtc::CritScope lock(&lock_); | 86 rtc::CritScope lock(&lock_); |
109 return ScreenCountUnlocked(); | 87 if (Initialize()) { |
| 88 return ScreenCountUnlocked(); |
| 89 } |
| 90 return 0; |
| 91 } |
| 92 |
| 93 DxgiDuplicatorController::Result |
| 94 DxgiDuplicatorController::DoDuplicate(DxgiFrame* frame, int monitor_id) { |
| 95 RTC_DCHECK(frame); |
| 96 rtc::CritScope lock(&lock_); |
| 97 |
| 98 // The dxgi components and APIs do not update the screen resolution without |
| 99 // a reinitialization. So we use the GetDC() function to retrieve the screen |
| 100 // resolution to decide whether dxgi components need to be reinitialized. |
| 101 // If the screen resolution changed, it's very likely the next Duplicate() |
| 102 // function call will fail because of a missing monitor or the frame size is |
| 103 // not enough to store the output. So we reinitialize dxgi components in-place |
| 104 // to avoid a capture failure. |
| 105 // But there is no guarantee GetDC() function returns the same resolution as |
| 106 // dxgi APIs, we still rely on dxgi components to return the output frame |
| 107 // size. |
| 108 // TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and |
| 109 // IDXGIOutputDuplication::GetDesc() can detect the resolution change without |
| 110 // reinitialization. |
| 111 if (resolution_change_detector_.IsChanged( |
| 112 GetScreenRect(kFullDesktopScreenId, std::wstring()).size())) { |
| 113 Deinitialize(); |
| 114 } |
| 115 |
| 116 if (!Initialize()) { |
| 117 // Cannot initialize COM components now, display mode may be changing. |
| 118 return Result::INITIALIZATION_FAILED; |
| 119 } |
| 120 |
| 121 if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) { |
| 122 return Result::FRAME_PREPARE_FAILED; |
| 123 } |
| 124 |
| 125 frame->frame()->mutable_updated_region()->Clear(); |
| 126 |
| 127 if (DoDuplicateUnlocked(frame->context(), monitor_id, frame->frame())) { |
| 128 return Result::SUCCEEDED; |
| 129 } |
| 130 if (monitor_id >= ScreenCountUnlocked()) { |
| 131 // It's a user error to provide a |monitor_id| larger than screen count. We |
| 132 // do not need to deinitialize. |
| 133 return Result::INVALID_MONITOR_ID; |
| 134 } |
| 135 |
| 136 // If the |monitor_id| is valid, but DoDuplicateUnlocked() failed, something |
| 137 // must be wrong from capturer APIs. We should Deinitialize(). |
| 138 Deinitialize(); |
| 139 return Result::DUPLICATION_FAILED; |
110 } | 140 } |
111 | 141 |
112 void DxgiDuplicatorController::Unregister(const Context* const context) { | 142 void DxgiDuplicatorController::Unregister(const Context* const context) { |
113 rtc::CritScope lock(&lock_); | 143 rtc::CritScope lock(&lock_); |
114 if (ContextExpired(context)) { | 144 if (ContextExpired(context)) { |
115 // The Context has not been setup after a recent initialization, so it | 145 // The Context has not been setup after a recent initialization, so it |
116 // should not been registered in duplicators. | 146 // should not been registered in duplicators. |
117 return; | 147 return; |
118 } | 148 } |
119 for (size_t i = 0; i < duplicators_.size(); i++) { | 149 for (size_t i = 0; i < duplicators_.size(); i++) { |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 } | 217 } |
188 | 218 |
189 void DxgiDuplicatorController::Deinitialize() { | 219 void DxgiDuplicatorController::Deinitialize() { |
190 desktop_rect_ = DesktopRect(); | 220 desktop_rect_ = DesktopRect(); |
191 duplicators_.clear(); | 221 duplicators_.clear(); |
192 resolution_change_detector_.Reset(); | 222 resolution_change_detector_.Reset(); |
193 } | 223 } |
194 | 224 |
195 bool DxgiDuplicatorController::ContextExpired( | 225 bool DxgiDuplicatorController::ContextExpired( |
196 const Context* const context) const { | 226 const Context* const context) const { |
| 227 RTC_DCHECK(context); |
197 return context->identity_ != identity_ || | 228 return context->identity_ != identity_ || |
198 context->contexts_.size() != duplicators_.size(); | 229 context->contexts_.size() != duplicators_.size(); |
199 } | 230 } |
200 | 231 |
201 void DxgiDuplicatorController::Setup(Context* context) { | 232 void DxgiDuplicatorController::Setup(Context* context) { |
202 if (ContextExpired(context)) { | 233 if (ContextExpired(context)) { |
| 234 RTC_DCHECK(context); |
203 context->contexts_.clear(); | 235 context->contexts_.clear(); |
204 context->contexts_.resize(duplicators_.size()); | 236 context->contexts_.resize(duplicators_.size()); |
205 for (size_t i = 0; i < duplicators_.size(); i++) { | 237 for (size_t i = 0; i < duplicators_.size(); i++) { |
206 duplicators_[i].Setup(&context->contexts_[i]); | 238 duplicators_[i].Setup(&context->contexts_[i]); |
207 } | 239 } |
208 context->identity_ = identity_; | 240 context->identity_ = identity_; |
209 } | 241 } |
210 } | 242 } |
211 | 243 |
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, | 244 bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, |
242 int monitor_id, | 245 int monitor_id, |
243 SharedDesktopFrame* target) { | 246 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); | 247 Setup(context); |
258 | 248 |
259 if (!EnsureFrameCaptured(context, target)) { | 249 if (!EnsureFrameCaptured(context, target)) { |
260 return false; | 250 return false; |
261 } | 251 } |
262 | 252 |
263 bool result = false; | 253 bool result = false; |
264 if (monitor_id < 0) { | 254 if (monitor_id < 0) { |
265 // Capture entire screen. | 255 // Capture entire screen. |
266 result = DoDuplicateAll(context, target); | 256 result = DoDuplicateAll(context, target); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 | 297 |
308 int64_t DxgiDuplicatorController::GetNumFramesCaptured() const { | 298 int64_t DxgiDuplicatorController::GetNumFramesCaptured() const { |
309 int64_t min = INT64_MAX; | 299 int64_t min = INT64_MAX; |
310 for (const auto& duplicator : duplicators_) { | 300 for (const auto& duplicator : duplicators_) { |
311 min = std::min(min, duplicator.GetNumFramesCaptured()); | 301 min = std::min(min, duplicator.GetNumFramesCaptured()); |
312 } | 302 } |
313 | 303 |
314 return min; | 304 return min; |
315 } | 305 } |
316 | 306 |
317 int DxgiDuplicatorController::ScreenCountUnlocked() { | 307 DesktopSize DxgiDuplicatorController::desktop_size() const { |
318 if (!Initialize()) { | 308 return DesktopSize(desktop_rect_.right(), desktop_rect_.bottom()); |
319 return 0; | 309 } |
| 310 |
| 311 DesktopRect DxgiDuplicatorController::ScreenRect(int id) const { |
| 312 RTC_DCHECK(id >= 0); |
| 313 for (size_t i = 0; i < duplicators_.size(); i++) { |
| 314 if (id >= duplicators_[i].screen_count()) { |
| 315 id -= duplicators_[i].screen_count(); |
| 316 } else { |
| 317 return duplicators_[i].ScreenRect(id); |
| 318 } |
320 } | 319 } |
| 320 return DesktopRect(); |
| 321 } |
| 322 |
| 323 int DxgiDuplicatorController::ScreenCountUnlocked() const { |
321 int result = 0; | 324 int result = 0; |
322 for (auto& duplicator : duplicators_) { | 325 for (auto& duplicator : duplicators_) { |
323 result += duplicator.screen_count(); | 326 result += duplicator.screen_count(); |
324 } | 327 } |
325 return result; | 328 return result; |
326 } | 329 } |
327 | 330 |
| 331 DesktopSize DxgiDuplicatorController::SelectedDesktopSize( |
| 332 int monitor_id) const { |
| 333 if (monitor_id < 0) { |
| 334 return desktop_size(); |
| 335 } |
| 336 |
| 337 return ScreenRect(monitor_id).size(); |
| 338 } |
| 339 |
328 bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, | 340 bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, |
329 SharedDesktopFrame* target) { | 341 SharedDesktopFrame* target) { |
330 // On a modern system, the FPS / monitor refresh rate is usually larger than | 342 // 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. | 343 // or equal to 60. So 17 milliseconds is enough to capture at least one frame. |
332 const int64_t ms_per_frame = 17; | 344 const int64_t ms_per_frame = 17; |
333 // Skips the first frame to ensure a full frame refresh has happened before | 345 // Skips the first frame to ensure a full frame refresh has happened before |
334 // this function returns. | 346 // this function returns. |
335 const int64_t frames_to_skip = 1; | 347 const int64_t frames_to_skip = 1; |
336 // The total time out milliseconds for this function. If we cannot get enough | 348 // 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 | 349 // 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; | 382 return false; |
371 } | 383 } |
372 if (rtc::TimeMillis() - start_ms > timeout_ms) { | 384 if (rtc::TimeMillis() - start_ms > timeout_ms) { |
373 return false; | 385 return false; |
374 } | 386 } |
375 } | 387 } |
376 return true; | 388 return true; |
377 } | 389 } |
378 | 390 |
379 } // namespace webrtc | 391 } // namespace webrtc |
OLD | NEW |