Chromium Code Reviews| 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..5c03c329bb2df3e8636afa178047ac68a4e9d97a |
| --- /dev/null |
| +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_container.cc |
| @@ -0,0 +1,270 @@ |
| +/* |
| + * 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" |
| + |
| +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() = default; |
| + |
| +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 (!duplicators_.empty()) { |
| + return true; |
| + } |
| + } |
| + |
| + if (initializing_.Enter()) { |
|
Sergey Ulanov
2016/07/28 22:15:32
Looking at this again I'm still not sure you reall
Hzj_jie
2016/07/29 02:12:23
Yes, I was wrong, this logic is out-of-date. As we
|
| + AutoExit exit(&initializing_); |
| + rtc::CritScope lock(&lock_); |
| + if (!duplicators_.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 !duplicators_.empty(); |
| + } |
| +} |
| + |
| +bool DxgiDuplicatorContainer::DxgiContextExpired( |
| + const DxgiContext* const context) const { |
| + return context->identity_ != identity_ || |
| + context->contexts_.size() != duplicators_.size(); |
| +} |
| + |
| +void DxgiDuplicatorContainer::Setup(DxgiContext* context) { |
| + rtc::CritScope lock(&lock_); |
| + if (duplicators_.empty()) { |
| + // The most recent initialization has failed. So we do not waste time to |
| + // setup DxgiContext, it will be setup again later. |
| + return; |
| + } |
| + if (DxgiContextExpired(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_; |
| + } |
| +} |
| + |
| +void DxgiDuplicatorContainer::Unregister(const DxgiContext* const context) { |
| + rtc::CritScope lock(&lock_); |
| + if (DxgiContextExpired(context)) { |
| + // The DxgiContext 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]); |
| + } |
| +} |
| + |
| +void DxgiDuplicatorContainer::Deinitialize() { |
| + desktop_rect_ = DesktopRect(); |
| + duplicators_.clear(); |
| +} |
| + |
| +bool DxgiDuplicatorContainer::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]); |
|
Sergey Ulanov
2016/07/28 22:15:32
Please don't use push_back() instead of emplace_ba
Hzj_jie
2016/07/29 02:12:23
Oh, this emplace_back is expected, as devices[i] 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 != nullptr) { |
|
Sergey Ulanov
2016/07/28 22:15:32
nit: s/ != nullptr//
Hzj_jie
2016/07/29 02:12:23
Done.
|
| + dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); |
| + ReleaseDC(nullptr, hdc); |
| + } |
| + |
| + identity_++; |
| + 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())) { |
|
Sergey Ulanov
2016/07/28 22:15:32
nit: s/last_frame != nullptr/last_frame/
Sergey Ulanov
2016/07/28 22:15:32
Replace with a DCHECK()
Hzj_jie
2016/07/29 02:12:23
Done.
Hzj_jie
2016/07/29 02:12:23
Returns false here can help consumers to decide wh
|
| + return false; |
| + } |
| + target->mutable_updated_region()->Clear(); |
| + rtc::CritScope lock(&lock_); |
| + if (duplicators_.empty() || DxgiContextExpired(context)) { |
| + // 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, |
|
Sergey Ulanov
2016/07/28 22:15:32
call this DuplicateScreen()?
https://google.github
Hzj_jie
2016/07/29 02:12:23
To match the design doc, changed to DuplicateMonit
|
| + int id, |
|
Sergey Ulanov
2016/07/28 22:15:32
screen_id?
Hzj_jie
2016/07/29 02:12:23
Done.
|
| + DesktopFrame* target, |
| + const DesktopFrame* last_frame) { |
|
Sergey Ulanov
2016/07/28 22:15:32
swap the last two arguments, see https://google.gi
Hzj_jie
2016/07/29 02:12:23
Order changed, but last_frame may be nullptr.
|
| + RTC_DCHECK(target); |
| + RTC_DCHECK(id >= 0); |
| + if (last_frame != nullptr && !target->size().equals(last_frame->size())) { |
|
Sergey Ulanov
2016/07/28 22:15:32
Replace this with a DCHECK.
Hzj_jie
2016/07/29 02:12:23
Same as above.
|
| + return false; |
| + } |
| + target->mutable_updated_region()->Clear(); |
| + rtc::CritScope lock(&lock_); |
| + if (duplicators_.empty() || DxgiContextExpired(context)) { |
| + // Next Prepare call will initialize duplicators_ or context. |
| + return false; |
| + } |
| + 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(). This is a user error, so we do not need to |
| + // deinitialize. |
| + return false; |
| +} |
| + |
| +} // namespace webrtc |