Index: webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc |
diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..46a345846e668299a4203925d5e75f7dd163c6e1 |
--- /dev/null |
+++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc |
@@ -0,0 +1,240 @@ |
+/* |
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h" |
+ |
+#include <windows.h> |
+ |
+#include <algorithm> |
+ |
+#include "webrtc/base/checks.h" |
+ |
+namespace webrtc { |
+ |
+DxgiDuplicatorController::Context::~Context() { |
+ DxgiDuplicatorController::Instance()->Unregister(this); |
+} |
+ |
+// static |
+DxgiDuplicatorController* DxgiDuplicatorController::Instance() { |
+ // The static instance won't be deleted to ensure it can be used by other |
+ // threads even during program exiting. |
+ static DxgiDuplicatorController* instance = new DxgiDuplicatorController(); |
+ return instance; |
+} |
+ |
+DxgiDuplicatorController::DxgiDuplicatorController() = default; |
+ |
+DxgiDuplicatorController::~DxgiDuplicatorController() { |
+ rtc::CritScope lock(&lock_); |
+ Deinitialize(); |
+} |
+ |
+bool DxgiDuplicatorController::IsSupported() { |
+ rtc::CritScope lock(&lock_); |
+ return Initialize(); |
+} |
+ |
+DesktopVector DxgiDuplicatorController::dpi() { |
+ rtc::CritScope lock(&lock_); |
+ if (Initialize()) { |
+ return dpi_; |
+ } |
+ return DesktopVector(); |
+} |
+ |
+DesktopRect DxgiDuplicatorController::desktop_rect() { |
+ rtc::CritScope lock(&lock_); |
+ if (Initialize()) { |
+ return desktop_rect_; |
+ } |
+ return DesktopRect(); |
+} |
+ |
+DesktopSize DxgiDuplicatorController::desktop_size() { |
+ DesktopRect rect = desktop_rect(); |
+ return DesktopSize(rect.right(), rect.bottom()); |
+} |
+ |
+DesktopRect DxgiDuplicatorController::ScreenRect(int id) { |
+ RTC_DCHECK(id >= 0); |
+ rtc::CritScope lock(&lock_); |
+ if (!Initialize()) { |
+ return DesktopRect(); |
+ } |
+ for (size_t i = 0; i < duplicators_.size(); i++) { |
+ if (id >= duplicators_[i].screen_count()) { |
+ id -= duplicators_[i].screen_count(); |
+ } else { |
+ return duplicators_[i].ScreenRect(id); |
+ } |
+ } |
+ return DesktopRect(); |
+} |
+ |
+int DxgiDuplicatorController::ScreenCount() { |
+ rtc::CritScope lock(&lock_); |
+ if (!Initialize()) { |
+ return 0; |
+ } |
+ int result = 0; |
+ for (auto& duplicator : duplicators_) { |
+ result += duplicator.screen_count(); |
+ } |
+ return result; |
+} |
+ |
+void DxgiDuplicatorController::Unregister(const Context* const context) { |
+ rtc::CritScope lock(&lock_); |
+ if (ContextExpired(context)) { |
+ // The Context has not been setup after a recent initialization, so it |
+ // should not been registered in duplicators. |
+ return; |
+ } |
+ for (size_t i = 0; i < duplicators_.size(); i++) { |
+ duplicators_[i].Unregister(&context->contexts_[i]); |
+ } |
+} |
+ |
+bool DxgiDuplicatorController::Initialize() { |
+ if (!duplicators_.empty()) { |
+ return true; |
+ } |
+ |
+ if (DoInitialize()) { |
+ return true; |
+ } |
+ Deinitialize(); |
+ return false; |
+} |
+ |
+bool DxgiDuplicatorController::DoInitialize() { |
+ RTC_DCHECK(desktop_rect_.is_empty()); |
+ RTC_DCHECK(duplicators_.empty()); |
+ |
+ std::vector<D3dDevice> devices = D3dDevice::EnumDevices(); |
+ if (devices.empty()) { |
+ return false; |
+ } |
+ |
+ for (size_t i = 0; i < devices.size(); i++) { |
+ duplicators_.emplace_back(devices[i]); |
+ if (!duplicators_.back().Initialize()) { |
+ return false; |
+ } |
+ if (desktop_rect_.is_empty()) { |
+ desktop_rect_ = duplicators_.back().desktop_rect(); |
+ } else { |
+ const DesktopRect& left = desktop_rect_; |
+ const DesktopRect& right = duplicators_.back().desktop_rect(); |
+ desktop_rect_ = |
+ DesktopRect::MakeLTRB(std::min(left.left(), right.left()), |
+ std::min(left.top(), right.top()), |
+ std::max(left.right(), right.right()), |
+ std::max(left.bottom(), right.bottom())); |
+ } |
+ } |
+ |
+ HDC hdc = GetDC(nullptr); |
+ // Use old DPI value if failed. |
+ if (hdc) { |
+ dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); |
+ ReleaseDC(nullptr, hdc); |
+ } |
+ |
+ identity_++; |
+ return true; |
+} |
+ |
+void DxgiDuplicatorController::Deinitialize() { |
+ desktop_rect_ = DesktopRect(); |
+ duplicators_.clear(); |
+} |
+ |
+bool DxgiDuplicatorController::ContextExpired( |
+ const Context* const context) const { |
+ return context->identity_ != identity_ || |
+ context->contexts_.size() != duplicators_.size(); |
+} |
+ |
+void DxgiDuplicatorController::Setup(Context* context) { |
+ if (ContextExpired(context)) { |
+ context->contexts_.clear(); |
+ context->contexts_.resize(duplicators_.size()); |
+ for (size_t i = 0; i < duplicators_.size(); i++) { |
+ duplicators_[i].Setup(&context->contexts_[i]); |
+ } |
+ context->identity_ = identity_; |
+ } |
+} |
+ |
+bool DxgiDuplicatorController::Duplicate(Context* context, |
+ const DesktopFrame* last_frame, |
+ DesktopFrame* target) { |
+ return DoDuplicate(context, -1, last_frame, target); |
+} |
+ |
+bool DxgiDuplicatorController::DuplicateMonitor(Context* context, |
+ int monitor_id, |
+ const DesktopFrame* last_frame, |
+ DesktopFrame* target) { |
+ RTC_DCHECK_GE(monitor_id, 0); |
+ return DoDuplicate(context, monitor_id, last_frame, target); |
+} |
+ |
+bool DxgiDuplicatorController::DoDuplicate(Context* context, |
+ int monitor_id, |
+ const DesktopFrame* last_frame, |
+ DesktopFrame* target) { |
+ RTC_DCHECK(target); |
+ if (last_frame && !target->size().equals(last_frame->size())) { |
+ return false; |
+ } |
+ target->mutable_updated_region()->Clear(); |
+ rtc::CritScope lock(&lock_); |
+ if (!Initialize()) { |
+ // Cannot initialize COM components now, display mode may be changing. |
+ return false; |
+ } |
+ Setup(context); |
+ if (monitor_id < 0) { |
+ // Capture entire screen. |
+ for (size_t i = 0; i < duplicators_.size(); i++) { |
+ if (!duplicators_[i].Duplicate(&context->contexts_[i], last_frame, |
+ target)) { |
+ Deinitialize(); |
+ return false; |
+ } |
+ } |
+ target->set_dpi(dpi()); |
+ return true; |
+ } |
+ |
+ // Capture one monitor. |
+ for (size_t i = 0; i < duplicators_.size() && i < context->contexts_.size(); |
+ i++) { |
+ if (monitor_id >= duplicators_[i].screen_count()) { |
+ monitor_id -= duplicators_[i].screen_count(); |
+ } else { |
+ if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id, |
+ last_frame, target)) { |
+ target->set_dpi(dpi()); |
+ return true; |
+ } |
+ Deinitialize(); |
+ return false; |
+ } |
+ } |
+ // id >= ScreenCount(). This is a user error, so we do not need to |
+ // deinitialize. |
+ return false; |
+} |
+ |
+} // namespace webrtc |