blob: 0276f6b94155065c3b9898789332c4da8ba63165 [file] [log] [blame]
/*
* 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,
SharedDesktopFrame* target) {
return DoDuplicate(context, -1, target);
}
bool DxgiDuplicatorController::DuplicateMonitor(Context* context,
int monitor_id,
SharedDesktopFrame* target) {
RTC_DCHECK_GE(monitor_id, 0);
return DoDuplicate(context, monitor_id, target);
}
bool DxgiDuplicatorController::DoDuplicate(Context* context,
int monitor_id,
SharedDesktopFrame* target) {
RTC_DCHECK(target);
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], 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,
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