Index: webrtc/modules/desktop_capture/win/dxgi_duplicator_container.cc |
diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_container.cc b/webrtc/modules/desktop_capture/win/dxgi_duplicator_container.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f2c27c2fd623120fb3f5add8f7d560c305da9c08 |
--- /dev/null |
+++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_container.cc |
@@ -0,0 +1,247 @@ |
+/* |
+ * 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_container.h" |
+ |
+#include <windows.h> |
+ |
+#include <algorithm> |
+ |
+#include "webrtc/base/checks.h" |
+#include "webrtc/base/timeutils.h" |
+ |
+namespace webrtc { |
+ |
+bool DxgiDuplicatorContainer::SingleEntry::Enter() { |
+ rtc::CritScope lock(&lock_); |
+ if (entered_) { |
+ return false; |
+ } |
+ entered_ = true; |
+ return true; |
+} |
+ |
+void DxgiDuplicatorContainer::SingleEntry::Exit() { |
+ rtc::CritScope lock(&lock_); |
+ RTC_DCHECK(entered_); |
+ entered_ = false; |
+} |
+ |
+bool DxgiDuplicatorContainer::SingleEntry::Hold() const { |
+ rtc::CritScope lock(&lock_); |
+ return entered_; |
+} |
+ |
+class DxgiDuplicatorContainer::AutoExit { |
+ public: |
+ explicit AutoExit(DxgiDuplicatorContainer::SingleEntry* gate) : gate_(gate) { |
+ RTC_DCHECK(gate_); |
+ } |
+ |
+ ~AutoExit() { gate_->Exit(); } |
+ |
+ private: |
+ DxgiDuplicatorContainer::SingleEntry* gate_; |
+}; |
+ |
+// static |
+DxgiDuplicatorContainer* DxgiDuplicatorContainer::Instance() { |
+ // The static instance won't be deleted to ensure it can be used by other |
+ // threads even during program exiting. |
+ static DxgiDuplicatorContainer* instance = new DxgiDuplicatorContainer(); |
+ return instance; |
+} |
+ |
+DxgiDuplicatorContainer::~DxgiDuplicatorContainer() { |
+ rtc::CritScope lock(&lock_); |
+ Deinitialize(); |
+} |
+ |
+bool DxgiDuplicatorContainer::Prepare(DxgiContext* context) { |
+ if (Prepare()) { |
+ Setup(context); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool DxgiDuplicatorContainer::Prepare() { |
+ // If the instance has been initialized already, does nothing and returns |
+ // true. |
+ { |
+ rtc::CritScope lock(&lock_); |
+ if (!devices_.empty()) { |
+ return true; |
+ } |
+ } |
+ |
+ if (initializing_.Enter()) { |
+ AutoExit exit(&initializing_); |
+ rtc::CritScope lock(&lock_); |
+ if (!devices_.empty()) { |
+ // Another thread has initialized this instance before this thread reaches |
+ // line initializing_.Enter(), so does nothing and returns true. |
+ return true; |
+ } |
+ |
+ if (DoInitialize()) { |
+ return true; |
+ } |
+ |
+ Deinitialize(); |
+ return false; |
+ } else { |
+ // Some other thread is initializing, wait for its finish and check the |
+ // result. |
+ while (initializing_.Hold()) { |
+ rtc::CritScope lock(&lock_); |
+ } |
+ rtc::CritScope lock(&lock_); |
+ return !devices_.empty(); |
+ } |
+} |
+ |
+void DxgiDuplicatorContainer::Setup(DxgiContext* context) { |
+ rtc::CritScope lock(&lock_); |
+ if (context->initialize_time_nanos_ <= initialize_time_nanos_) { |
+ context->contexts_.clear(); |
+ context->contexts_.resize(devices_.size()); |
+ for (size_t i = 0; i < devices_.size(); i++) { |
+ duplicators_[i].Setup(&context->contexts_[i]); |
+ } |
+ // Times may not change, so following assignment can make sure we won't |
+ // setup this context instance again. |
+ context->initialize_time_nanos_ = initialize_time_nanos_ + 1; |
+ } |
+} |
+ |
+void DxgiDuplicatorContainer::Deinitialize() { |
+ desktop_rect_ = DesktopRect(); |
+ duplicators_.clear(); |
+ devices_.clear(); |
+} |
+ |
+bool DxgiDuplicatorContainer::DoInitialize() { |
+ RTC_DCHECK(desktop_rect_.is_empty()); |
+ RTC_DCHECK(duplicators_.empty()); |
+ RTC_DCHECK(devices_.empty()); |
+ |
+ devices_ = D3dDevice::EnumDevices(); |
+ if (devices_.empty()) { |
+ return false; |
+ } |
+ |
+ for (size_t i = 0; i < devices_.size(); i++) { |
+ duplicators_.emplace_back(); |
+ if (!duplicators_.back().Initialize(devices_[i])) { |
+ 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 != nullptr) { |
+ dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); |
+ ReleaseDC(nullptr, hdc); |
+ } |
+ |
+ initialize_time_nanos_ = rtc::TimeNanos(); |
+ return true; |
+} |
+ |
+DesktopRect DxgiDuplicatorContainer::ScreenRect(int id) const { |
+ RTC_DCHECK(id >= 0); |
+ rtc::CritScope lock(&lock_); |
+ 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 DxgiDuplicatorContainer::ScreenCount() const { |
+ rtc::CritScope lock(&lock_); |
+ int result = 0; |
+ for (auto& duplicator : duplicators_) { |
+ result += duplicator.screen_count(); |
+ } |
+ return result; |
+} |
+ |
+bool DxgiDuplicatorContainer::Duplicate(DxgiContext* context, |
+ DesktopFrame* target, |
+ const DesktopFrame* last_frame) { |
+ RTC_DCHECK(target); |
+ if (last_frame != nullptr && !target->size().equals(last_frame->size())) { |
+ return false; |
+ } |
+ target->mutable_updated_region()->Clear(); |
+ rtc::CritScope lock(&lock_); |
+ if (duplicators_.empty() || |
+ context->contexts_.size() != duplicators_.size()) { |
+ // Next Prepare call will initialize duplicators_ or context. |
+ return false; |
+ } |
+ for (size_t i = 0; i < duplicators_.size(); i++) { |
+ if (!duplicators_[i].Duplicate( |
+ &context->contexts_[i], target, last_frame)) { |
+ Deinitialize(); |
+ return false; |
+ } |
+ } |
+ target->set_dpi(dpi()); |
+ return true; |
+} |
+ |
+bool DxgiDuplicatorContainer::Duplicate(DxgiContext* context, |
+ int id, |
+ DesktopFrame* target, |
+ const DesktopFrame* last_frame) { |
+ RTC_DCHECK(target); |
+ RTC_DCHECK(id >= 0); |
+ if (last_frame != nullptr && !target->size().equals(last_frame->size())) { |
+ return false; |
+ } |
+ target->mutable_updated_region()->Clear(); |
+ rtc::CritScope lock(&lock_); |
+ for (size_t i = 0; |
+ i < duplicators_.size() && i < context->contexts_.size(); |
+ i++) { |
+ if (id >= duplicators_[i].screen_count()) { |
+ id -= duplicators_[i].screen_count(); |
+ } else { |
+ if (duplicators_[i].Duplicate( |
+ &context->contexts_[i], id, target, last_frame)) { |
+ target->set_dpi(dpi()); |
+ return true; |
+ } |
+ Deinitialize(); |
+ return false; |
+ } |
+ } |
+ // id >= ScreenCount() || context->contexts_.size() != devices_.size() |
+ return false; |
+} |
+ |
+} // namespace webrtc |