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