| 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 |