[Chromoting] Improve DirectX capturer to support multiple outputs
Current DirectX capturer cannot capture multiple video cards or monitors. But according to DXGI desktop duplication sample (https://goo.gl/An0L9l), we can capture multiple video cards and monitors by duplicating them one by one. So instead of one IDXGIOutputDuplication instance, this change creates an IDXGIOutputDuplication instance for each monitor, and merge the output into one DesktopFrame. Several other changes are also included, 1. Add supports to DXGI_OUTDUPL_DESC.DesktopImageInSystemMemory. When this flag is true, we won't copy its content to staging buffer. 2. Capture one monitor instead of entire screen. Above changes make the logic complex. But with some refactor work, the logic is not disordered. Please refer to the doc @ https://goo.gl/hU1ifG. BUG=314516 Review-Url: https://codereview.webrtc.org/2099123002 Cr-Commit-Position: refs/heads/master@{#13684}
This commit is contained in:
@ -72,8 +72,22 @@ source_set("desktop_capture") {
|
|||||||
"shared_memory.h",
|
"shared_memory.h",
|
||||||
"win/cursor.cc",
|
"win/cursor.cc",
|
||||||
"win/cursor.h",
|
"win/cursor.h",
|
||||||
|
"win/d3d_device.cc",
|
||||||
|
"win/d3d_device.h",
|
||||||
"win/desktop.cc",
|
"win/desktop.cc",
|
||||||
"win/desktop.h",
|
"win/desktop.h",
|
||||||
|
"win/dxgi_adapter_duplicator.cc",
|
||||||
|
"win/dxgi_adapter_duplicator.h",
|
||||||
|
"win/dxgi_duplicator_controller.cc",
|
||||||
|
"win/dxgi_duplicator_controller.h",
|
||||||
|
"win/dxgi_output_duplicator.cc",
|
||||||
|
"win/dxgi_output_duplicator.h",
|
||||||
|
"win/dxgi_texture.cc",
|
||||||
|
"win/dxgi_texture.h",
|
||||||
|
"win/dxgi_texture_mapping.cc",
|
||||||
|
"win/dxgi_texture_mapping.h",
|
||||||
|
"win/dxgi_texture_staging.cc",
|
||||||
|
"win/dxgi_texture_staging.h",
|
||||||
"win/scoped_gdi_object.h",
|
"win/scoped_gdi_object.h",
|
||||||
"win/scoped_thread_desktop.cc",
|
"win/scoped_thread_desktop.cc",
|
||||||
"win/scoped_thread_desktop.h",
|
"win/scoped_thread_desktop.h",
|
||||||
@ -124,7 +138,10 @@ source_set("desktop_capture") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_win) {
|
if (is_win) {
|
||||||
libs = [ "d3d11.lib" ]
|
libs = [
|
||||||
|
"d3d11.lib",
|
||||||
|
"dxgi.lib",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
configs += [ "../..:common_config" ]
|
configs += [ "../..:common_config" ]
|
||||||
|
|||||||
@ -69,8 +69,22 @@
|
|||||||
"shared_memory.h",
|
"shared_memory.h",
|
||||||
"win/cursor.cc",
|
"win/cursor.cc",
|
||||||
"win/cursor.h",
|
"win/cursor.h",
|
||||||
|
"win/d3d_device.cc",
|
||||||
|
"win/d3d_device.h",
|
||||||
"win/desktop.cc",
|
"win/desktop.cc",
|
||||||
"win/desktop.h",
|
"win/desktop.h",
|
||||||
|
"win/dxgi_adapter_duplicator.cc",
|
||||||
|
"win/dxgi_adapter_duplicator.h",
|
||||||
|
"win/dxgi_duplicator_controller.cc",
|
||||||
|
"win/dxgi_duplicator_controller.h",
|
||||||
|
"win/dxgi_output_duplicator.cc",
|
||||||
|
"win/dxgi_output_duplicator.h",
|
||||||
|
"win/dxgi_texture.cc",
|
||||||
|
"win/dxgi_texture.h",
|
||||||
|
"win/dxgi_texture_mapping.cc",
|
||||||
|
"win/dxgi_texture_mapping.h",
|
||||||
|
"win/dxgi_texture_staging.cc",
|
||||||
|
"win/dxgi_texture_staging.h",
|
||||||
"win/scoped_gdi_object.h",
|
"win/scoped_gdi_object.h",
|
||||||
"win/scoped_thread_desktop.cc",
|
"win/scoped_thread_desktop.cc",
|
||||||
"win/scoped_thread_desktop.h",
|
"win/scoped_thread_desktop.h",
|
||||||
@ -137,6 +151,7 @@
|
|||||||
'VCLinkerTool': {
|
'VCLinkerTool': {
|
||||||
'AdditionalDependencies': [
|
'AdditionalDependencies': [
|
||||||
'd3d11.lib',
|
'd3d11.lib',
|
||||||
|
'dxgi.lib',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/screen_capturer.h"
|
#include "webrtc/modules/desktop_capture/screen_capturer.h"
|
||||||
|
|
||||||
@ -75,8 +76,8 @@ ACTION_P(SaveUniquePtrArg, dest) {
|
|||||||
TEST_F(ScreenCapturerTest, GetScreenListAndSelectScreen) {
|
TEST_F(ScreenCapturerTest, GetScreenListAndSelectScreen) {
|
||||||
webrtc::ScreenCapturer::ScreenList screens;
|
webrtc::ScreenCapturer::ScreenList screens;
|
||||||
EXPECT_TRUE(capturer_->GetScreenList(&screens));
|
EXPECT_TRUE(capturer_->GetScreenList(&screens));
|
||||||
for(webrtc::ScreenCapturer::ScreenList::iterator it = screens.begin();
|
for (webrtc::ScreenCapturer::ScreenList::iterator it = screens.begin();
|
||||||
it != screens.end(); ++it) {
|
it != screens.end(); ++it) {
|
||||||
EXPECT_TRUE(capturer_->SelectScreen(it->id));
|
EXPECT_TRUE(capturer_->SelectScreen(it->id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,6 +145,40 @@ TEST_F(ScreenCapturerTest, UseMagnifier) {
|
|||||||
ASSERT_TRUE(frame);
|
ASSERT_TRUE(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ScreenCapturerTest, UseDirectxCapturer) {
|
||||||
|
DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
|
||||||
|
options.set_allow_directx_capturer(true);
|
||||||
|
capturer_.reset(ScreenCapturer::Create(options));
|
||||||
|
|
||||||
|
std::unique_ptr<DesktopFrame> frame;
|
||||||
|
EXPECT_CALL(callback_,
|
||||||
|
OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
|
||||||
|
.WillOnce(SaveUniquePtrArg(&frame));
|
||||||
|
|
||||||
|
capturer_->Start(&callback_);
|
||||||
|
capturer_->Capture(DesktopRegion());
|
||||||
|
ASSERT_TRUE(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ScreenCapturerTest, UseDirectxCapturerWithSharedBuffers) {
|
||||||
|
DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
|
||||||
|
options.set_allow_directx_capturer(true);
|
||||||
|
capturer_.reset(ScreenCapturer::Create(options));
|
||||||
|
|
||||||
|
std::unique_ptr<DesktopFrame> frame;
|
||||||
|
EXPECT_CALL(callback_,
|
||||||
|
OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
|
||||||
|
.WillOnce(SaveUniquePtrArg(&frame));
|
||||||
|
|
||||||
|
capturer_->Start(&callback_);
|
||||||
|
capturer_->SetSharedMemoryFactory(
|
||||||
|
std::unique_ptr<SharedMemoryFactory>(new FakeSharedMemoryFactory()));
|
||||||
|
capturer_->Capture(DesktopRegion());
|
||||||
|
ASSERT_TRUE(frame);
|
||||||
|
ASSERT_TRUE(frame->shared_memory());
|
||||||
|
EXPECT_EQ(frame->shared_memory()->id(), kTestSharedMemoryId);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // defined(WEBRTC_WIN)
|
#endif // defined(WEBRTC_WIN)
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -24,7 +24,7 @@ namespace webrtc {
|
|||||||
ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
|
ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
|
||||||
std::unique_ptr<ScreenCapturer> capturer;
|
std::unique_ptr<ScreenCapturer> capturer;
|
||||||
if (options.allow_directx_capturer() &&
|
if (options.allow_directx_capturer() &&
|
||||||
ScreenCapturerWinDirectx::Initialize()) {
|
ScreenCapturerWinDirectx::IsSupported()) {
|
||||||
capturer.reset(new ScreenCapturerWinDirectx(options));
|
capturer.reset(new ScreenCapturerWinDirectx(options));
|
||||||
} else {
|
} else {
|
||||||
capturer.reset(new ScreenCapturerWinGdi(options));
|
capturer.reset(new ScreenCapturerWinGdi(options));
|
||||||
|
|||||||
93
webrtc/modules/desktop_capture/win/d3d_device.cc
Normal file
93
webrtc/modules/desktop_capture/win/d3d_device.cc
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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/d3d_device.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "webrtc/system_wrappers/include/logging.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
D3dDevice::D3dDevice() = default;
|
||||||
|
D3dDevice::~D3dDevice() = default;
|
||||||
|
|
||||||
|
bool D3dDevice::Initialize(const ComPtr<IDXGIAdapter>& adapter) {
|
||||||
|
dxgi_adapter_ = adapter;
|
||||||
|
if (!dxgi_adapter_) {
|
||||||
|
LOG(LS_WARNING) << "An empty IDXGIAdapter instance has been received.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D_FEATURE_LEVEL feature_level;
|
||||||
|
_com_error error = D3D11CreateDevice(
|
||||||
|
adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
|
||||||
|
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
|
||||||
|
nullptr, 0, D3D11_SDK_VERSION, d3d_device_.GetAddressOf(), &feature_level,
|
||||||
|
context_.GetAddressOf());
|
||||||
|
if (error.Error() != S_OK || !d3d_device_ || !context_) {
|
||||||
|
LOG(LS_WARNING) << "D3D11CreateDeivce returns error "
|
||||||
|
<< error.ErrorMessage() << " with code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature_level < D3D_FEATURE_LEVEL_11_0) {
|
||||||
|
LOG(LS_WARNING) << "D3D11CreateDevice returns an instance without DirectX "
|
||||||
|
"11 support, level "
|
||||||
|
<< feature_level;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = _com_error(d3d_device_.As(&dxgi_device_));
|
||||||
|
if (error.Error() != S_OK || !dxgi_device_) {
|
||||||
|
LOG(LS_WARNING) << "ID3D11Device is not an implementation of IDXGIDevice, "
|
||||||
|
"this usually means the system does not support DirectX "
|
||||||
|
"11";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::vector<D3dDevice> D3dDevice::EnumDevices() {
|
||||||
|
ComPtr<IDXGIFactory1> factory;
|
||||||
|
_com_error error = _com_error(
|
||||||
|
CreateDXGIFactory1(__uuidof(IDXGIFactory1),
|
||||||
|
reinterpret_cast<void**>(factory.GetAddressOf())));
|
||||||
|
if (error.Error() != S_OK || !factory) {
|
||||||
|
return std::vector<D3dDevice>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<D3dDevice> result;
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
ComPtr<IDXGIAdapter> adapter;
|
||||||
|
error = _com_error(factory->EnumAdapters(i, adapter.GetAddressOf()));
|
||||||
|
if (error.Error() == S_OK) {
|
||||||
|
D3dDevice device;
|
||||||
|
if (!device.Initialize(adapter)) {
|
||||||
|
return std::vector<D3dDevice>();
|
||||||
|
}
|
||||||
|
result.push_back(std::move(device));
|
||||||
|
} else if (error.Error() == DXGI_ERROR_NOT_FOUND) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
LOG(LS_WARNING) << "IDXGIFactory1::EnumAdapters returns an unexpected "
|
||||||
|
"error "
|
||||||
|
<< error.ErrorMessage() << " with code " << error.Error();
|
||||||
|
return std::vector<D3dDevice>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
57
webrtc/modules/desktop_capture/win/d3d_device.h
Normal file
57
webrtc/modules/desktop_capture/win/d3d_device.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
|
||||||
|
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
#include <D3D11.h>
|
||||||
|
#include <DXGI.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// A wrapper of ID3D11Device and its corresponding context and IDXGIAdapter.
|
||||||
|
// This class represents one video card in the system.
|
||||||
|
class D3dDevice {
|
||||||
|
public:
|
||||||
|
~D3dDevice();
|
||||||
|
|
||||||
|
ID3D11Device* d3d_device() const { return d3d_device_.Get(); }
|
||||||
|
|
||||||
|
ID3D11DeviceContext* context() const { return context_.Get(); }
|
||||||
|
|
||||||
|
IDXGIDevice* dxgi_device() const { return dxgi_device_.Get(); }
|
||||||
|
|
||||||
|
IDXGIAdapter* dxgi_adapter() const { return dxgi_adapter_.Get(); }
|
||||||
|
|
||||||
|
// Returns all D3dDevice instances on the system. Returns an empty vector if
|
||||||
|
// anything wrong.
|
||||||
|
static std::vector<D3dDevice> EnumDevices();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Instances of D3dDevice should only be created by EnumDevices() static
|
||||||
|
// function.
|
||||||
|
D3dDevice();
|
||||||
|
|
||||||
|
// Initialize the D3dDevice from an IDXGIAdapter.
|
||||||
|
bool Initialize(const Microsoft::WRL::ComPtr<IDXGIAdapter>& adapter);
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11Device> d3d_device_;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context_;
|
||||||
|
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device_;
|
||||||
|
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
|
||||||
145
webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
Normal file
145
webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* 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_adapter_duplicator.h"
|
||||||
|
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <DXGI.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/base/logging.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool IsValidRect(const RECT& rect) {
|
||||||
|
return rect.left >= 0 && rect.top >= 0 && rect.right > rect.left &&
|
||||||
|
rect.bottom > rect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device)
|
||||||
|
: device_(device) {}
|
||||||
|
|
||||||
|
DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default;
|
||||||
|
|
||||||
|
bool DxgiAdapterDuplicator::Initialize() {
|
||||||
|
if (DoInitialize()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
duplicators_.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiAdapterDuplicator::DoInitialize() {
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
ComPtr<IDXGIOutput> output;
|
||||||
|
_com_error error =
|
||||||
|
device_.dxgi_adapter()->EnumOutputs(i, output.GetAddressOf());
|
||||||
|
if (error.Error() == DXGI_ERROR_NOT_FOUND) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.Error() != S_OK || !output) {
|
||||||
|
LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returns an unexpected "
|
||||||
|
"result "
|
||||||
|
<< error.ErrorMessage() << " with error code"
|
||||||
|
<< error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXGI_OUTPUT_DESC desc;
|
||||||
|
error = _com_error(output->GetDesc(&desc));
|
||||||
|
if (error.Error() == S_OK) {
|
||||||
|
if (desc.AttachedToDesktop && IsValidRect(desc.DesktopCoordinates)) {
|
||||||
|
ComPtr<IDXGIOutput1> output1;
|
||||||
|
error = _com_error(output.As(&output1));
|
||||||
|
if (error.Error() != S_OK || !output1) {
|
||||||
|
LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, "
|
||||||
|
"this usually means the system does not support "
|
||||||
|
"DirectX 11";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
duplicators_.emplace_back(device_, output1, desc);
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(LS_WARNING) << "Failed to get output description of device " << i
|
||||||
|
<< ", ignore.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiAdapterDuplicator::Setup(Context* context) {
|
||||||
|
RTC_DCHECK(context->contexts.empty());
|
||||||
|
context->contexts.resize(duplicators_.size());
|
||||||
|
for (size_t i = 0; i < duplicators_.size(); i++) {
|
||||||
|
duplicators_[i].Setup(&context->contexts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiAdapterDuplicator::Unregister(const Context* const context) {
|
||||||
|
RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
|
||||||
|
for (size_t i = 0; i < duplicators_.size(); i++) {
|
||||||
|
duplicators_[i].Unregister(&context->contexts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiAdapterDuplicator::Duplicate(Context* context,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target) {
|
||||||
|
RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
|
||||||
|
for (size_t i = 0; i < duplicators_.size(); i++) {
|
||||||
|
if (!duplicators_[i].Duplicate(&context->contexts[i], last_frame,
|
||||||
|
duplicators_[i].desktop_rect().top_left(),
|
||||||
|
target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context,
|
||||||
|
int monitor_id,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target) {
|
||||||
|
RTC_DCHECK(monitor_id >= 0 &&
|
||||||
|
monitor_id < static_cast<int>(duplicators_.size()) &&
|
||||||
|
context->contexts.size() == duplicators_.size());
|
||||||
|
return duplicators_[monitor_id].Duplicate(
|
||||||
|
&context->contexts[monitor_id], last_frame, DesktopVector(), target);
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
|
||||||
|
RTC_DCHECK(id >= 0 && id < static_cast<int>(duplicators_.size()));
|
||||||
|
return duplicators_[id].desktop_rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
88
webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h
Normal file
88
webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
|
||||||
|
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/d3d_device.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// A container of DxgiOutputDuplicators to duplicate monitors attached to a
|
||||||
|
// single video card.
|
||||||
|
class DxgiAdapterDuplicator {
|
||||||
|
public:
|
||||||
|
struct Context {
|
||||||
|
// Child DxgiOutputDuplicator::Context belongs to this
|
||||||
|
// DxgiAdapterDuplicator::Context.
|
||||||
|
std::vector<DxgiOutputDuplicator::Context> contexts;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates an instance of DxgiAdapterDuplicator from a D3dDevice. Only
|
||||||
|
// DxgiDuplicatorController can create an instance.
|
||||||
|
explicit DxgiAdapterDuplicator(const D3dDevice& device);
|
||||||
|
|
||||||
|
// Move constructor, to make it possible to store instances of
|
||||||
|
// DxgiAdapterDuplicator in std::vector<>.
|
||||||
|
DxgiAdapterDuplicator(DxgiAdapterDuplicator&& other);
|
||||||
|
|
||||||
|
// Initializes the DxgiAdapterDuplicator from a D3dDevice.
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
// Sequential calls Duplicate function of all the DxgiOutputDuplicators owned
|
||||||
|
// by this instance.
|
||||||
|
bool Duplicate(Context* context,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target);
|
||||||
|
|
||||||
|
// Captures one monitor and writes into target. |monitor_id| should be between
|
||||||
|
// [0, screen_count()).
|
||||||
|
bool DuplicateMonitor(Context* context,
|
||||||
|
int monitor_id,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target);
|
||||||
|
|
||||||
|
// Returns desktop rect covered by this DxgiAdapterDuplicator.
|
||||||
|
DesktopRect desktop_rect() const { return desktop_rect_; }
|
||||||
|
|
||||||
|
// Returns the size of one screen owned by this DxgiAdapterDuplicator. |id|
|
||||||
|
// should be between [0, screen_count()).
|
||||||
|
DesktopRect ScreenRect(int id) const;
|
||||||
|
|
||||||
|
// Returns the count of screens owned by this DxgiAdapterDuplicator. These
|
||||||
|
// screens can be retrieved by an interger in the range of
|
||||||
|
// [0, screen_count()).
|
||||||
|
int screen_count() const { return static_cast<int>(duplicators_.size()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class DxgiDuplicatorController;
|
||||||
|
|
||||||
|
bool DoInitialize();
|
||||||
|
|
||||||
|
void Setup(Context* context);
|
||||||
|
|
||||||
|
void Unregister(const Context* const context);
|
||||||
|
|
||||||
|
const D3dDevice device_;
|
||||||
|
std::vector<DxgiOutputDuplicator> duplicators_;
|
||||||
|
DesktopRect desktop_rect_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
|
||||||
240
webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc
Normal file
240
webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc
Normal file
@ -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
|
||||||
172
webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
Normal file
172
webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/criticalsection.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/d3d_device.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// A controller for all the objects we need to call Windows DirectX capture APIs
|
||||||
|
// It's a singleton because, only one IDXGIOutputDuplication instance per
|
||||||
|
// monitor is allowed per application.
|
||||||
|
//
|
||||||
|
// Consumers should create a DxgiDuplicatorController::Context and keep it
|
||||||
|
// throughout their lifetime, and pass it when calling Duplicate(). Consumers
|
||||||
|
// can also call IsSupported() to determine whether the system supports DXGI
|
||||||
|
// duplicator or not. If a previous IsSupported() function call returns true,
|
||||||
|
// but a later Duplicate() returns false, this usually means the display mode is
|
||||||
|
// changing. Consumers should retry after a while. (Typically 50 milliseconds,
|
||||||
|
// but according to hardware performance, this time may vary.)
|
||||||
|
//
|
||||||
|
// This class is normally used with double buffering, e.g. as in
|
||||||
|
// ScreenCapturerWinDirectx, but it should work with consumers with one buffer,
|
||||||
|
// i.e. consumers can always send nullptr for |last_frame|. Some minor changes
|
||||||
|
// in DxgiOutputDuplicator class are nice to have to reduce size of data to copy
|
||||||
|
// (Commented in dxgi_output_duplicator.cc). But this class won't work
|
||||||
|
// with three or more buffers, the updated region merging logic will be broken
|
||||||
|
// in such scenarios. If a consumer does have this requirement, one can always
|
||||||
|
// send a new Context instance to Duplicate() function to force duplicator to
|
||||||
|
// treat it as a new consumer.
|
||||||
|
class DxgiDuplicatorController {
|
||||||
|
public:
|
||||||
|
// A context to store the status of a single consumer of
|
||||||
|
// DxgiDuplicatorController.
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
// Unregister this Context instance from all Dxgi duplicators during
|
||||||
|
// destructing.
|
||||||
|
~Context();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class DxgiDuplicatorController;
|
||||||
|
|
||||||
|
// A Context will have an exactly same |identity_| as
|
||||||
|
// DxgiDuplicatorController, to ensure it has been correctly setted up after
|
||||||
|
// each DxgiDuplicatorController::Initialize().
|
||||||
|
int identity_ = 0;
|
||||||
|
|
||||||
|
// Child DxgiAdapterDuplicator::Context belongs to this Context.
|
||||||
|
std::vector<DxgiAdapterDuplicator::Context> contexts_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the singleton instance of DxgiDuplicatorController.
|
||||||
|
static DxgiDuplicatorController* Instance();
|
||||||
|
|
||||||
|
// Destructs current instance. We need to make sure COM components and their
|
||||||
|
// containers are destructed in correct order.
|
||||||
|
~DxgiDuplicatorController();
|
||||||
|
|
||||||
|
// Detects whether the system supports DXGI based capturer.
|
||||||
|
bool IsSupported();
|
||||||
|
|
||||||
|
// Captures current screen and writes into target. Since we are using double
|
||||||
|
// buffering, |last_frame|.updated_region() is used to represent the not
|
||||||
|
// updated regions in current |target| frame, which should also be copied this
|
||||||
|
// time.
|
||||||
|
// TODO(zijiehe): Windows cannot guarantee the frames returned by each
|
||||||
|
// IDXGIOutputDuplication are synchronized. But we are using a totally
|
||||||
|
// different threading model than the way Windows suggested, it's hard to
|
||||||
|
// synchronize them manually. But we should find a way to do it.
|
||||||
|
bool Duplicate(Context* context,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target);
|
||||||
|
|
||||||
|
// Captures one monitor and writes into target. |monitor_id| should >= 0. If
|
||||||
|
// |monitor_id| is greater than the total screen count of all the Duplicators,
|
||||||
|
// this function returns false.
|
||||||
|
bool DuplicateMonitor(Context* context,
|
||||||
|
int monitor_id,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target);
|
||||||
|
|
||||||
|
// Returns dpi of current system. Returns an empty DesktopVector if system
|
||||||
|
// does not support DXGI based capturer.
|
||||||
|
DesktopVector dpi();
|
||||||
|
|
||||||
|
// Returns entire desktop size. Returns an empty DesktopRect if system does
|
||||||
|
// not support DXGI based capturer.
|
||||||
|
DesktopRect desktop_rect();
|
||||||
|
|
||||||
|
// Returns a DesktopSize to cover entire desktop_rect. This may be different
|
||||||
|
// than desktop_rect().size(), since top-left screen does not need to start
|
||||||
|
// from (0, 0).
|
||||||
|
DesktopSize desktop_size();
|
||||||
|
|
||||||
|
// Returns the size of one screen. |monitor_id| should be >= 0. If system does
|
||||||
|
// not support DXGI based capturer, or |monitor_id| is greater than the total
|
||||||
|
// screen count of all the Duplicators, this function returns an empty
|
||||||
|
// DesktopRect.
|
||||||
|
DesktopRect ScreenRect(int id);
|
||||||
|
|
||||||
|
// Returns the count of screens on the system. These screens can be retrieved
|
||||||
|
// by an integer in the range of [0, ScreenCount()). If system does not
|
||||||
|
// support DXGI based capturer, this function returns 0.
|
||||||
|
int ScreenCount();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Context calls private Unregister(Context*) function during
|
||||||
|
// destructing.
|
||||||
|
friend class Context;
|
||||||
|
|
||||||
|
// A private constructor to ensure consumers to use
|
||||||
|
// DxgiDuplicatorController::Instance().
|
||||||
|
DxgiDuplicatorController();
|
||||||
|
|
||||||
|
// Unregisters Context from this instance and all DxgiAdapterDuplicator(s)
|
||||||
|
// it owns.
|
||||||
|
void Unregister(const Context* const context);
|
||||||
|
|
||||||
|
// All functions below should be called in |lock_| locked scope.
|
||||||
|
|
||||||
|
// If current instance has not been initialized, executes DoInitialize
|
||||||
|
// function, and returns initialize result. Otherwise directly returns true.
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
bool DoInitialize();
|
||||||
|
|
||||||
|
// Clears all COM components referred by this instance. So next Duplicate()
|
||||||
|
// call will eventually initialize this instance again.
|
||||||
|
void Deinitialize();
|
||||||
|
|
||||||
|
// A helper function to check whether a Context has been expired.
|
||||||
|
bool ContextExpired(const Context* const context) const;
|
||||||
|
|
||||||
|
// Updates Context if needed.
|
||||||
|
void Setup(Context* context);
|
||||||
|
|
||||||
|
// Do the real duplication work. |monitor_id < 0| to capture entire screen.
|
||||||
|
bool DoDuplicate(Context* context,
|
||||||
|
int monitor_id,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
DesktopFrame* target);
|
||||||
|
|
||||||
|
// This lock must be locked whenever accessing any of the following objects.
|
||||||
|
rtc::CriticalSection lock_;
|
||||||
|
|
||||||
|
// A self-incremented integer to compare with the one in Context, to
|
||||||
|
// ensure a Context has been initialized after DxgiDuplicatorController.
|
||||||
|
int identity_ = 0;
|
||||||
|
DesktopRect desktop_rect_;
|
||||||
|
DesktopVector dpi_;
|
||||||
|
std::vector<DxgiAdapterDuplicator> duplicators_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
|
||||||
304
webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
Normal file
304
webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
* 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_output_duplicator.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unknwn.h>
|
||||||
|
#include <DXGIFormat.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/base/logging.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_texture_staging.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Timeout for AcquireNextFrame() call.
|
||||||
|
const int kAcquireTimeoutMs = 10;
|
||||||
|
|
||||||
|
DesktopRect RECTToDesktopRect(const RECT& rect) {
|
||||||
|
return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
|
||||||
|
const ComPtr<IDXGIOutput1>& output,
|
||||||
|
const DXGI_OUTPUT_DESC& desc)
|
||||||
|
: device_(device),
|
||||||
|
output_(output),
|
||||||
|
desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) {
|
||||||
|
RTC_DCHECK(output_);
|
||||||
|
RTC_DCHECK(!desktop_rect_.is_empty());
|
||||||
|
RTC_DCHECK(desktop_rect_.left() >= 0 && desktop_rect_.top() >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) =
|
||||||
|
default;
|
||||||
|
|
||||||
|
DxgiOutputDuplicator::~DxgiOutputDuplicator() {
|
||||||
|
if (duplication_) {
|
||||||
|
duplication_->ReleaseFrame();
|
||||||
|
}
|
||||||
|
texture_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiOutputDuplicator::Initialize() {
|
||||||
|
if (DuplicateOutput()) {
|
||||||
|
if (desc_.DesktopImageInSystemMemory) {
|
||||||
|
texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get()));
|
||||||
|
} else {
|
||||||
|
texture_.reset(new DxgiTextureStaging(desktop_rect_, device_));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
duplication_.Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiOutputDuplicator::DuplicateOutput() {
|
||||||
|
RTC_DCHECK(!duplication_);
|
||||||
|
_com_error error =
|
||||||
|
output_->DuplicateOutput(static_cast<IUnknown*>(device_.d3d_device()),
|
||||||
|
duplication_.GetAddressOf());
|
||||||
|
if (error.Error() != S_OK || !duplication_) {
|
||||||
|
LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
|
||||||
|
<< error.ErrorMessage() << ", with code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&desc_, 0, sizeof(desc_));
|
||||||
|
duplication_->GetDesc(&desc_);
|
||||||
|
if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
|
||||||
|
LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
|
||||||
|
"format, which is required by downstream components, "
|
||||||
|
"format is "
|
||||||
|
<< desc_.ModeDesc.Format;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
|
||||||
|
static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
|
||||||
|
LOG(LS_ERROR) << "IDXGIDuplicateOutput does not return a same size as its "
|
||||||
|
"IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
|
||||||
|
<< desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
|
||||||
|
<< ", size returned by IDXGIOutput1 is "
|
||||||
|
<< desktop_rect_.width() << " x " << desktop_rect_.height();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiOutputDuplicator::ReleaseFrame() {
|
||||||
|
RTC_DCHECK(duplication_);
|
||||||
|
_com_error error = duplication_->ReleaseFrame();
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication, "
|
||||||
|
"error"
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiOutputDuplicator::Duplicate(Context* context,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
const DesktopVector offset,
|
||||||
|
DesktopFrame* target) {
|
||||||
|
RTC_DCHECK(duplication_);
|
||||||
|
RTC_DCHECK(texture_);
|
||||||
|
RTC_DCHECK(target);
|
||||||
|
DXGI_OUTDUPL_FRAME_INFO frame_info;
|
||||||
|
memset(&frame_info, 0, sizeof(frame_info));
|
||||||
|
ComPtr<IDXGIResource> resource;
|
||||||
|
_com_error error = duplication_->AcquireNextFrame(
|
||||||
|
kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
|
||||||
|
if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
|
||||||
|
LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
|
||||||
|
<< ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to merge updated region with the one from last frame, since current
|
||||||
|
// frame contains the content one frame before. Note, this is for double
|
||||||
|
// buffering implementation, as what we have in ScreenCapturerWinDirectx. If
|
||||||
|
// a consumer uses single buffering, we should clear context->updated_region
|
||||||
|
// after it has been merged to updated_region.
|
||||||
|
DesktopRegion updated_region = context->updated_region;
|
||||||
|
if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) {
|
||||||
|
DetectUpdatedRegion(frame_info, offset, &context->updated_region);
|
||||||
|
SpreadContextChange(context);
|
||||||
|
updated_region.AddRegion(context->updated_region);
|
||||||
|
if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DesktopFrame& source = texture_->AsDesktopFrame();
|
||||||
|
DesktopRect target_rect(DesktopRect::MakeSize(target->size()));
|
||||||
|
for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
|
||||||
|
it.Advance()) {
|
||||||
|
if (!target_rect.ContainsRect(it.rect())) {
|
||||||
|
// target size is not large enough to copy the pixel from texture.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
target->CopyPixelsFrom(source, it.rect().top_left().subtract(offset),
|
||||||
|
it.rect());
|
||||||
|
}
|
||||||
|
target->mutable_updated_region()->AddRegion(updated_region);
|
||||||
|
return texture_->Release() && ReleaseFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_frame != nullptr) {
|
||||||
|
// DxgiOutputDuplicatorContainer::Duplicate() makes sure target size and
|
||||||
|
// last frame size are consistent.
|
||||||
|
RTC_DCHECK(target->size().equals(last_frame->size()));
|
||||||
|
// No change since last frame or AcquireNextFrame() timed out, we will
|
||||||
|
// export last frame to the target.
|
||||||
|
context->updated_region.Clear();
|
||||||
|
for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
|
||||||
|
it.Advance()) {
|
||||||
|
target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect());
|
||||||
|
}
|
||||||
|
target->mutable_updated_region()->AddRegion(updated_region);
|
||||||
|
}
|
||||||
|
// If AcquireNextFrame() failed with timeout error, we do not need to release
|
||||||
|
// the frame.
|
||||||
|
return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(
|
||||||
|
const DesktopVector offset) {
|
||||||
|
DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size()));
|
||||||
|
result.Translate(offset);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiOutputDuplicator::DetectUpdatedRegion(
|
||||||
|
const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
const DesktopVector offset,
|
||||||
|
DesktopRegion* updated_region) {
|
||||||
|
if (DoDetectUpdatedRegion(frame_info, updated_region)) {
|
||||||
|
updated_region->Translate(offset.x(), offset.y());
|
||||||
|
// Make sure even a region returned by Windows API is out of the scope of
|
||||||
|
// desktop_rect_, we still won't export it to the target DesktopFrame.
|
||||||
|
updated_region->IntersectWith(TranslatedDesktopRect(offset));
|
||||||
|
} else {
|
||||||
|
updated_region->SetRect(TranslatedDesktopRect(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
|
||||||
|
const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
DesktopRegion* updated_region) {
|
||||||
|
RTC_DCHECK(updated_region);
|
||||||
|
updated_region->Clear();
|
||||||
|
if (frame_info.TotalMetadataBufferSize == 0) {
|
||||||
|
// This should not happen, since frame_info.AccumulatedFrames > 0.
|
||||||
|
LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
|
||||||
|
"but TotalMetadataBufferSize == 0";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.capacity() < frame_info.TotalMetadataBufferSize) {
|
||||||
|
metadata.clear(); // Avoid data copy
|
||||||
|
metadata.reserve(frame_info.TotalMetadataBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT buff_size = 0;
|
||||||
|
DXGI_OUTDUPL_MOVE_RECT* move_rects =
|
||||||
|
reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
|
||||||
|
size_t move_rects_count = 0;
|
||||||
|
_com_error error = _com_error(duplication_->GetFrameMoveRects(
|
||||||
|
static_cast<UINT>(metadata.capacity()), move_rects, &buff_size));
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
LOG(LS_ERROR) << "Failed to get move rectangles, error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
|
||||||
|
|
||||||
|
RECT* dirty_rects = reinterpret_cast<RECT*>(metadata.data() + buff_size);
|
||||||
|
size_t dirty_rects_count = 0;
|
||||||
|
error = _com_error(duplication_->GetFrameDirtyRects(
|
||||||
|
static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects,
|
||||||
|
&buff_size));
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
LOG(LS_ERROR) << "Failed to get dirty rectangles, error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dirty_rects_count = buff_size / sizeof(RECT);
|
||||||
|
|
||||||
|
while (move_rects_count > 0) {
|
||||||
|
updated_region->AddRect(DesktopRect::MakeXYWH(
|
||||||
|
move_rects->SourcePoint.x, move_rects->SourcePoint.y,
|
||||||
|
move_rects->DestinationRect.right - move_rects->DestinationRect.left,
|
||||||
|
move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
|
||||||
|
updated_region->AddRect(DesktopRect::MakeLTRB(
|
||||||
|
move_rects->DestinationRect.left, move_rects->DestinationRect.top,
|
||||||
|
move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
|
||||||
|
move_rects++;
|
||||||
|
move_rects_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (dirty_rects_count > 0) {
|
||||||
|
updated_region->AddRect(
|
||||||
|
DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top,
|
||||||
|
dirty_rects->right, dirty_rects->bottom));
|
||||||
|
dirty_rects++;
|
||||||
|
dirty_rects_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiOutputDuplicator::Setup(Context* context) {
|
||||||
|
RTC_DCHECK(context->updated_region.is_empty());
|
||||||
|
// Always copy entire monitor during the first Duplicate() function call.
|
||||||
|
context->updated_region.AddRect(desktop_rect_);
|
||||||
|
for (size_t i = 0; i < contexts_.size(); i++) {
|
||||||
|
if (contexts_[i] == nullptr) {
|
||||||
|
contexts_[i] = context;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts_.push_back(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiOutputDuplicator::Unregister(const Context* const context) {
|
||||||
|
for (size_t i = 0; i < contexts_.size(); i++) {
|
||||||
|
if (contexts_[i] == context) {
|
||||||
|
contexts_[i] = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RTC_NOTREACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) {
|
||||||
|
for (Context* dest : contexts_) {
|
||||||
|
if (dest != source) {
|
||||||
|
dest->updated_region.AddRegion(source->updated_region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
128
webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h
Normal file
128
webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
|
||||||
|
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
#include <DXGI.h>
|
||||||
|
#include <DXGI1_2.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/criticalsection.h"
|
||||||
|
#include "webrtc/base/thread_annotations.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/d3d_device.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_texture.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Duplicates the content on one IDXGIOutput, i.e. one monitor attached to one
|
||||||
|
// video card. None of functions in this class is thread-safe.
|
||||||
|
// TODO(zijiehe): Understand the meaning of rotation.
|
||||||
|
class DxgiOutputDuplicator {
|
||||||
|
public:
|
||||||
|
struct Context {
|
||||||
|
// The updated region DxgiOutputDuplicator::DetectUpdatedRegion() output
|
||||||
|
// during last Duplicate() function call. It's a DesktopRegion translated by
|
||||||
|
// offset of each DxgiOutputDuplicator instance.
|
||||||
|
DesktopRegion updated_region;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates an instance of DxgiOutputDuplicator from a D3dDevice and one of its
|
||||||
|
// IDXGIOutput1. Caller must maintain the lifetime of device, to make sure it
|
||||||
|
// outlives this instance. Only DxgiAdapterDuplicator can create an instance.
|
||||||
|
DxgiOutputDuplicator(const D3dDevice& device,
|
||||||
|
const Microsoft::WRL::ComPtr<IDXGIOutput1>& output,
|
||||||
|
const DXGI_OUTPUT_DESC& desc);
|
||||||
|
|
||||||
|
// To allow this class to work with vector.
|
||||||
|
DxgiOutputDuplicator(DxgiOutputDuplicator&& other);
|
||||||
|
|
||||||
|
// Destructs this instance. We need to make sure texture_ has been released
|
||||||
|
// before duplication_.
|
||||||
|
~DxgiOutputDuplicator();
|
||||||
|
|
||||||
|
// Initializes duplication_ object.
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
// Copies the content of current IDXGIOutput to the |target|. To improve the
|
||||||
|
// performance, this function copies only regions merged from
|
||||||
|
// |last_frame|.updated_region and DetectUpdatedRegion(). The |offset| decides
|
||||||
|
// the
|
||||||
|
// offset in the |target| where the content should be copied to. i.e. this
|
||||||
|
// function copies the content to the rectangle of (offset.x(), offset.y()) to
|
||||||
|
// (offset.x() + desktop_rect_.width(), offset.y() + desktop_rect_.height()).
|
||||||
|
// The |last_frame| is always expected to be translated by the same offset.
|
||||||
|
// Returns false in case of a failure.
|
||||||
|
bool Duplicate(Context* context,
|
||||||
|
const DesktopFrame* last_frame,
|
||||||
|
const DesktopVector offset,
|
||||||
|
DesktopFrame* target);
|
||||||
|
|
||||||
|
// Returns the desktop rect covered by this DxgiOutputDuplicator.
|
||||||
|
DesktopRect desktop_rect() const { return desktop_rect_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class DxgiAdapterDuplicator;
|
||||||
|
|
||||||
|
// Detects updated region translated by offset from IDXGIOutput1. This
|
||||||
|
// function will set the |updated_region| as entire DesktopRect starts from
|
||||||
|
// offset if it failed to execute Windows APIs.
|
||||||
|
void DetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
const DesktopVector offset,
|
||||||
|
DesktopRegion* updated_region);
|
||||||
|
|
||||||
|
// Returns untranslated updated region, which are directly returned by Windows
|
||||||
|
// APIs. Returns false in case of a failure.
|
||||||
|
bool DoDetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
DesktopRegion* updated_region);
|
||||||
|
|
||||||
|
bool ReleaseFrame();
|
||||||
|
|
||||||
|
// Initializes duplication_ instance. Expects duplication_ is in empty status.
|
||||||
|
// Returns false if system does not support IDXGIOutputDuplication.
|
||||||
|
bool DuplicateOutput();
|
||||||
|
|
||||||
|
// Returns a DesktopRect with the same size of desktop_size_, but translated
|
||||||
|
// by offset.
|
||||||
|
DesktopRect TranslatedDesktopRect(const DesktopVector offset);
|
||||||
|
|
||||||
|
void Setup(Context* context);
|
||||||
|
|
||||||
|
void Unregister(const Context* const context);
|
||||||
|
|
||||||
|
// Spreads changes from |context| to other registered Context(s) in
|
||||||
|
// contexts_.
|
||||||
|
void SpreadContextChange(const Context* const context);
|
||||||
|
|
||||||
|
const D3dDevice& device_;
|
||||||
|
const Microsoft::WRL::ComPtr<IDXGIOutput1> output_;
|
||||||
|
const DesktopRect desktop_rect_;
|
||||||
|
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> duplication_;
|
||||||
|
DXGI_OUTDUPL_DESC desc_;
|
||||||
|
std::vector<uint8_t> metadata;
|
||||||
|
std::unique_ptr<DxgiTexture> texture_;
|
||||||
|
|
||||||
|
// After each AcquireNextFrame() function call, updated_region_(s) of all
|
||||||
|
// active Context(s) need to be updated. Since they have missed the
|
||||||
|
// change this time. And during next Duplicate() function call, their
|
||||||
|
// updated_region_ will be merged and copied.
|
||||||
|
std::vector<Context*> contexts_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
|
||||||
47
webrtc/modules/desktop_capture/win/dxgi_texture.cc
Normal file
47
webrtc/modules/desktop_capture/win/dxgi_texture.cc
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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_texture.h"
|
||||||
|
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class DxgiDesktopFrame : public DesktopFrame {
|
||||||
|
public:
|
||||||
|
explicit DxgiDesktopFrame(const DxgiTexture& texture)
|
||||||
|
: DesktopFrame(texture.desktop_rect().size(),
|
||||||
|
texture.pitch(),
|
||||||
|
texture.bits(),
|
||||||
|
nullptr) {}
|
||||||
|
|
||||||
|
virtual ~DxgiDesktopFrame() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
DxgiTexture::DxgiTexture(const DesktopRect& desktop_rect)
|
||||||
|
: desktop_rect_(desktop_rect) {}
|
||||||
|
|
||||||
|
const DesktopFrame& DxgiTexture::AsDesktopFrame() {
|
||||||
|
if (!frame_) {
|
||||||
|
frame_.reset(new DxgiDesktopFrame(*this));
|
||||||
|
}
|
||||||
|
return *frame_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiTexture::Release() {
|
||||||
|
frame_.reset();
|
||||||
|
return DoRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
69
webrtc/modules/desktop_capture/win/dxgi_texture.h
Normal file
69
webrtc/modules/desktop_capture/win/dxgi_texture.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
|
||||||
|
|
||||||
|
#include <DXGI1_2.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class DesktopRegion;
|
||||||
|
|
||||||
|
// A texture copied or mapped from a DXGI_OUTDUPL_FRAME_INFO and IDXGIResource.
|
||||||
|
class DxgiTexture {
|
||||||
|
public:
|
||||||
|
// Creates a DxgiTexture instance, which represents the DesktopRect area of
|
||||||
|
// entire screen -- usually a monitor on the system.
|
||||||
|
explicit DxgiTexture(const DesktopRect& desktop_rect);
|
||||||
|
|
||||||
|
virtual ~DxgiTexture() = default;
|
||||||
|
|
||||||
|
// Copies selected regions of a frame represented by frame_info and resource.
|
||||||
|
// Returns false if anything wrong.
|
||||||
|
virtual bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
IDXGIResource* resource,
|
||||||
|
const DesktopRegion& region) = 0;
|
||||||
|
|
||||||
|
const DesktopRect& desktop_rect() const { return desktop_rect_; }
|
||||||
|
|
||||||
|
uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); }
|
||||||
|
|
||||||
|
int pitch() const { return static_cast<int>(rect_.Pitch); }
|
||||||
|
|
||||||
|
// Releases the resource currently holds by this instance. Returns false if
|
||||||
|
// anything wrong, and this instance should be deprecated in this state. bits,
|
||||||
|
// pitch and AsDesktopFrame are only valid after a success CopyFrom() call,
|
||||||
|
// but before Release() call.
|
||||||
|
bool Release();
|
||||||
|
|
||||||
|
// Returns a DesktopFrame snapshot of a DxgiTexture instance. This
|
||||||
|
// DesktopFrame is used to copy a DxgiTexture content to another DesktopFrame
|
||||||
|
// only. And it should not outlive its DxgiTexture instance.
|
||||||
|
const DesktopFrame& AsDesktopFrame();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DXGI_MAPPED_RECT rect_ = {0};
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool DoRelease() = 0;
|
||||||
|
|
||||||
|
const DesktopRect desktop_rect_;
|
||||||
|
std::unique_ptr<DesktopFrame> frame_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
|
||||||
57
webrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc
Normal file
57
webrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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_texture_mapping.h"
|
||||||
|
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <DXGI.h>
|
||||||
|
#include <DXGI1_2.h>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/system_wrappers/include/logging.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
DxgiTextureMapping::DxgiTextureMapping(const DesktopRect& desktop_rect,
|
||||||
|
IDXGIOutputDuplication* duplication)
|
||||||
|
: DxgiTexture(desktop_rect), duplication_(duplication) {
|
||||||
|
RTC_DCHECK(duplication_);
|
||||||
|
}
|
||||||
|
|
||||||
|
DxgiTextureMapping::~DxgiTextureMapping() = default;
|
||||||
|
|
||||||
|
bool DxgiTextureMapping::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
IDXGIResource* resource,
|
||||||
|
const DesktopRegion& region) {
|
||||||
|
RTC_DCHECK(resource && frame_info.AccumulatedFrames > 0);
|
||||||
|
rect_ = {0};
|
||||||
|
_com_error error = duplication_->MapDesktopSurface(&rect_);
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
rect_ = {0};
|
||||||
|
LOG(LS_ERROR) << "Failed to map the IDXGIOutputDuplication to a bitmap, "
|
||||||
|
"error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiTextureMapping::DoRelease() {
|
||||||
|
_com_error error = duplication_->UnMapDesktopSurface();
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
LOG(LS_ERROR) << "Failed to unmap the IDXGIOutputDuplication, error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
48
webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h
Normal file
48
webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
|
||||||
|
|
||||||
|
#include <D3D11.h>
|
||||||
|
#include <DXGI1_2.h>
|
||||||
|
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_texture.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// A DxgiTexture which directly maps bitmap from IDXGIResource. This class is
|
||||||
|
// used when DXGI_OUTDUPL_DESC.DesktopImageInSystemMemory is true. (This usually
|
||||||
|
// means the video card shares main memory with CPU, instead of having its own
|
||||||
|
// individual memory.)
|
||||||
|
class DxgiTextureMapping : public DxgiTexture {
|
||||||
|
public:
|
||||||
|
// Creates a DxgiTextureMapping instance. Caller must maintain the lifetime
|
||||||
|
// of input duplication to make sure it outlives this instance.
|
||||||
|
DxgiTextureMapping(const DesktopRect& desktop_rect,
|
||||||
|
IDXGIOutputDuplication* duplication);
|
||||||
|
|
||||||
|
~DxgiTextureMapping() override;
|
||||||
|
|
||||||
|
bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
IDXGIResource* resource,
|
||||||
|
const DesktopRegion& region) override;
|
||||||
|
|
||||||
|
bool DoRelease() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
IDXGIOutputDuplication* const duplication_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
|
||||||
149
webrtc/modules/desktop_capture/win/dxgi_texture_staging.cc
Normal file
149
webrtc/modules/desktop_capture/win/dxgi_texture_staging.cc
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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_texture_staging.h"
|
||||||
|
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <unknwn.h>
|
||||||
|
#include <DXGI.h>
|
||||||
|
#include <DXGI1_2.h>
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/system_wrappers/include/logging.h"
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
DxgiTextureStaging::DxgiTextureStaging(const DesktopRect& desktop_rect,
|
||||||
|
const D3dDevice& device)
|
||||||
|
: DxgiTexture(desktop_rect), device_(device) {}
|
||||||
|
|
||||||
|
DxgiTextureStaging::~DxgiTextureStaging() = default;
|
||||||
|
|
||||||
|
bool DxgiTextureStaging::InitializeStage(ID3D11Texture2D* texture) {
|
||||||
|
RTC_DCHECK(texture);
|
||||||
|
D3D11_TEXTURE2D_DESC desc = {0};
|
||||||
|
texture->GetDesc(&desc);
|
||||||
|
if (static_cast<int>(desc.Width) != desktop_rect().width() ||
|
||||||
|
static_cast<int>(desc.Height) != desktop_rect().height()) {
|
||||||
|
LOG(LS_ERROR) << "Texture size is not consistent with current DxgiTexture.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc.BindFlags = 0;
|
||||||
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||||
|
desc.MiscFlags = 0;
|
||||||
|
desc.Usage = D3D11_USAGE_STAGING;
|
||||||
|
if (stage_) {
|
||||||
|
AssertStageAndSurfaceAreSameObject();
|
||||||
|
D3D11_TEXTURE2D_DESC current_desc;
|
||||||
|
stage_->GetDesc(¤t_desc);
|
||||||
|
if (memcmp(&desc, ¤t_desc, sizeof(D3D11_TEXTURE2D_DESC)) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The descriptions are not consistent, we need to create a new
|
||||||
|
// ID3D11Texture2D instance.
|
||||||
|
stage_.Reset();
|
||||||
|
surface_.Reset();
|
||||||
|
} else {
|
||||||
|
RTC_DCHECK(!surface_);
|
||||||
|
}
|
||||||
|
|
||||||
|
_com_error error = _com_error(device_.d3d_device()->CreateTexture2D(
|
||||||
|
&desc, nullptr, stage_.GetAddressOf()));
|
||||||
|
if (error.Error() != S_OK || !stage_) {
|
||||||
|
LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage, error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = _com_error(stage_.As(&surface_));
|
||||||
|
if (error.Error() != S_OK || !surface_) {
|
||||||
|
LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface, error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DxgiTextureStaging::AssertStageAndSurfaceAreSameObject() {
|
||||||
|
ComPtr<IUnknown> left;
|
||||||
|
ComPtr<IUnknown> right;
|
||||||
|
bool left_result = SUCCEEDED(stage_.As(&left));
|
||||||
|
bool right_result = SUCCEEDED(surface_.As(&right));
|
||||||
|
RTC_DCHECK(left_result);
|
||||||
|
RTC_DCHECK(right_result);
|
||||||
|
RTC_DCHECK(left.Get() == right.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiTextureStaging::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
IDXGIResource* resource,
|
||||||
|
const DesktopRegion& region) {
|
||||||
|
RTC_DCHECK(resource && frame_info.AccumulatedFrames > 0);
|
||||||
|
ComPtr<ID3D11Texture2D> texture;
|
||||||
|
_com_error error = resource->QueryInterface(
|
||||||
|
__uuidof(ID3D11Texture2D),
|
||||||
|
reinterpret_cast<void**>(texture.GetAddressOf()));
|
||||||
|
if (error.Error() != S_OK || !texture) {
|
||||||
|
LOG(LS_ERROR) << "Failed to convert IDXGIResource to ID3D11Texture2D, "
|
||||||
|
"error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcquireNextFrame returns a CPU inaccessible IDXGIResource, so we need to
|
||||||
|
// copy it to a CPU accessible staging ID3D11Texture2D.
|
||||||
|
if (!InitializeStage(texture.Get())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DesktopRegion::Iterator it(region); !it.IsAtEnd(); it.Advance()) {
|
||||||
|
DesktopRect rect(it.rect());
|
||||||
|
rect.Translate(-desktop_rect().left(), -desktop_rect().top());
|
||||||
|
D3D11_BOX box;
|
||||||
|
box.left = rect.left();
|
||||||
|
box.top = rect.top();
|
||||||
|
box.right = rect.right();
|
||||||
|
box.bottom = rect.bottom();
|
||||||
|
box.front = 0;
|
||||||
|
box.back = 1;
|
||||||
|
device_.context()->CopySubresourceRegion(
|
||||||
|
static_cast<ID3D11Resource*>(stage_.Get()), 0, rect.left(), rect.top(),
|
||||||
|
0, static_cast<ID3D11Resource*>(texture.Get()), 0, &box);
|
||||||
|
}
|
||||||
|
|
||||||
|
rect_ = {0};
|
||||||
|
error = _com_error(surface_->Map(&rect_, DXGI_MAP_READ));
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
rect_ = {0};
|
||||||
|
LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap, error "
|
||||||
|
<< error.ErrorMessage() << ", code " << error.Error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DxgiTextureStaging::DoRelease() {
|
||||||
|
_com_error error = _com_error(surface_->Unmap());
|
||||||
|
if (error.Error() != S_OK) {
|
||||||
|
stage_.Reset();
|
||||||
|
surface_.Reset();
|
||||||
|
}
|
||||||
|
// If using staging mode, we only need to recreate ID3D11Texture2D instance.
|
||||||
|
// This will happen during next CopyFrom call. So this function always returns
|
||||||
|
// true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
68
webrtc/modules/desktop_capture/win/dxgi_texture_staging.h
Normal file
68
webrtc/modules/desktop_capture/win/dxgi_texture_staging.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
|
||||||
|
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
|
||||||
|
|
||||||
|
#include <wrl/client.h>
|
||||||
|
#include <D3D11.h>
|
||||||
|
#include <DXGI1_2.h>
|
||||||
|
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/d3d_device.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_texture.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// A pair of an ID3D11Texture2D and an IDXGISurface. We need an ID3D11Texture2D
|
||||||
|
// instance to copy GPU texture to RAM, but an IDXGISurface instance to map the
|
||||||
|
// texture into a bitmap buffer. These two instances are pointing to a same
|
||||||
|
// object.
|
||||||
|
//
|
||||||
|
// An ID3D11Texture2D is created by an ID3D11Device, so a DxgiTexture cannot be
|
||||||
|
// shared between two DxgiAdapterDuplicators.
|
||||||
|
class DxgiTextureStaging : public DxgiTexture {
|
||||||
|
public:
|
||||||
|
// Creates a DxgiTextureStaging instance. Caller must maintain the lifetime
|
||||||
|
// of input device to make sure it outlives this instance.
|
||||||
|
DxgiTextureStaging(const DesktopRect& desktop_rect, const D3dDevice& device);
|
||||||
|
|
||||||
|
~DxgiTextureStaging() override;
|
||||||
|
|
||||||
|
// Copies selected regions of a frame represented by frame_info and resource.
|
||||||
|
// Returns false if anything wrong.
|
||||||
|
bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||||
|
IDXGIResource* resource,
|
||||||
|
const DesktopRegion& region) override;
|
||||||
|
|
||||||
|
bool DoRelease() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false if
|
||||||
|
// it failed to execute Windows APIs, or the size of the texture is not
|
||||||
|
// consistent with desktop_rect.
|
||||||
|
bool InitializeStage(ID3D11Texture2D* texture);
|
||||||
|
|
||||||
|
// Makes sure stage_ and surface_ are always pointing to a same object.
|
||||||
|
// We need an ID3D11Texture2D instance for
|
||||||
|
// ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for
|
||||||
|
// IDXGISurface::Map.
|
||||||
|
void AssertStageAndSurfaceAreSameObject();
|
||||||
|
|
||||||
|
const DesktopRect desktop_rect_;
|
||||||
|
const D3dDevice& device_;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11Texture2D> stage_;
|
||||||
|
Microsoft::WRL::ComPtr<IDXGISurface> surface_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
|
||||||
@ -10,458 +10,25 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h"
|
#include "webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <utility>
|
||||||
|
|
||||||
#include <comdef.h>
|
|
||||||
#include <wincodec.h>
|
|
||||||
#include <wingdi.h>
|
|
||||||
#include <DXGI.h>
|
|
||||||
|
|
||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
#include "webrtc/base/criticalsection.h"
|
#include "webrtc/base/logging.h"
|
||||||
#include "webrtc/base/scoped_ref_ptr.h"
|
|
||||||
#include "webrtc/base/timeutils.h"
|
#include "webrtc/base/timeutils.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||||
#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
|
|
||||||
#include "webrtc/system_wrappers/include/atomic32.h"
|
|
||||||
#include "webrtc/system_wrappers/include/logging.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
using Microsoft::WRL::ComPtr;
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
namespace {
|
bool ScreenCapturerWinDirectx::IsSupported() {
|
||||||
|
// Forward IsSupported function call to DxgiDuplicatorController.
|
||||||
// Timeout for AcquireNextFrame() call.
|
return DxgiDuplicatorController::Instance()->IsSupported();
|
||||||
const int kAcquireTimeoutMs = 10;
|
|
||||||
|
|
||||||
// Wait time between two DuplicateOutput operations, DuplicateOutput may fail if
|
|
||||||
// display mode is changing.
|
|
||||||
const int kDuplicateOutputWaitMs = 50;
|
|
||||||
|
|
||||||
// How many times we attempt to DuplicateOutput before returning an error to
|
|
||||||
// upstream components.
|
|
||||||
const int kDuplicateOutputAttempts = 10;
|
|
||||||
|
|
||||||
rtc::GlobalLockPod g_initialize_lock;
|
|
||||||
|
|
||||||
// A container of all the objects we need to call Windows API. Note, one
|
|
||||||
// application can only have one IDXGIOutputDuplication instance, that's the
|
|
||||||
// reason the container is singleton.
|
|
||||||
struct DxgiContainer {
|
|
||||||
rtc::CriticalSection duplication_lock;
|
|
||||||
rtc::CriticalSection acquire_lock;
|
|
||||||
bool initialize_result GUARDED_BY(g_initialize_lock) = false;
|
|
||||||
ID3D11Device* device GUARDED_BY(g_initialize_lock) = nullptr;
|
|
||||||
ID3D11DeviceContext* context GUARDED_BY(g_initialize_lock) = nullptr;
|
|
||||||
IDXGIOutput1* output1 GUARDED_BY(g_initialize_lock) = nullptr;
|
|
||||||
ComPtr<IDXGIOutputDuplication> duplication
|
|
||||||
GUARDED_BY(duplication_lock);
|
|
||||||
DXGI_OUTDUPL_DESC duplication_desc;
|
|
||||||
std::vector<uint8_t> metadata GUARDED_BY(acquire_lock);
|
|
||||||
};
|
|
||||||
|
|
||||||
DxgiContainer* g_container GUARDED_BY(g_initialize_lock);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// A pair of an ID3D11Texture2D and an IDXGISurface. We need an
|
|
||||||
// ID3D11Texture2D instance to copy GPU texture to RAM, but an IDXGISurface to
|
|
||||||
// map the texture into a bitmap buffer. These two instances are always
|
|
||||||
// pointing to a same object.
|
|
||||||
// This class also has two DesktopRegions, one is the updated region from
|
|
||||||
// returned from Windows API, the other is the region intersects with the
|
|
||||||
// updated region of last frame.
|
|
||||||
//
|
|
||||||
// This class is not thread safe.
|
|
||||||
class ScreenCapturerWinDirectx::Texture {
|
|
||||||
public:
|
|
||||||
// Copy a frame represented by frame_info and resource. Returns false if
|
|
||||||
// anything wrong.
|
|
||||||
bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
|
||||||
IDXGIResource* resource,
|
|
||||||
const DesktopRegion& last_updated_region) {
|
|
||||||
if (!resource || frame_info.AccumulatedFrames == 0) {
|
|
||||||
// Nothing updated, but current data is still valid.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<ID3D11Texture2D> texture;
|
|
||||||
_com_error error = resource->QueryInterface(
|
|
||||||
__uuidof(ID3D11Texture2D),
|
|
||||||
reinterpret_cast<void**>(texture.GetAddressOf()));
|
|
||||||
if (error.Error() != S_OK || !texture) {
|
|
||||||
LOG(LS_ERROR) << "Failed to convert IDXGIResource to ID3D11Texture2D, "
|
|
||||||
"error "
|
|
||||||
<< error.ErrorMessage() << ", code " << error.Error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AcquireNextFrame returns a CPU inaccessible IDXGIResource, so we need to
|
|
||||||
// make a copy.
|
|
||||||
if (!InitializeStage(texture.Get())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
updated_region_.Clear();
|
|
||||||
if (!DetectUpdatedRegion(frame_info, &updated_region_)) {
|
|
||||||
updated_region_.SetRect(DesktopRect::MakeSize(size()));
|
|
||||||
}
|
|
||||||
// We need to copy changed area in both this frame and last frame, since
|
|
||||||
// currently this frame stores the bitmap of the one before last frame.
|
|
||||||
copied_region_.Clear();
|
|
||||||
copied_region_.AddRegion(updated_region_);
|
|
||||||
copied_region_.AddRegion(last_updated_region);
|
|
||||||
copied_region_.IntersectWith(DesktopRect::MakeSize(size()));
|
|
||||||
|
|
||||||
for (DesktopRegion::Iterator it(copied_region_);
|
|
||||||
!it.IsAtEnd();
|
|
||||||
it.Advance()) {
|
|
||||||
D3D11_BOX box;
|
|
||||||
box.left = it.rect().left();
|
|
||||||
box.top = it.rect().top();
|
|
||||||
box.right = it.rect().right();
|
|
||||||
box.bottom = it.rect().bottom();
|
|
||||||
box.front = 0;
|
|
||||||
box.back = 1;
|
|
||||||
g_container->context->CopySubresourceRegion(
|
|
||||||
static_cast<ID3D11Resource*>(stage_.Get()),
|
|
||||||
0, it.rect().left(), it.rect().top(), 0,
|
|
||||||
static_cast<ID3D11Resource*>(texture.Get()),
|
|
||||||
0, &box);
|
|
||||||
}
|
|
||||||
|
|
||||||
rect_ = {0};
|
|
||||||
error = _com_error(surface_->Map(&rect_, DXGI_MAP_READ));
|
|
||||||
if (error.Error() != S_OK) {
|
|
||||||
rect_ = {0};
|
|
||||||
LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap, error "
|
|
||||||
<< error.ErrorMessage() << ", code " << error.Error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// surface_->Unmap() will be called next time we capture an image to avoid
|
|
||||||
// memory copy without shared_memory.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); }
|
|
||||||
int pitch() const { return static_cast<int>(rect_.Pitch); }
|
|
||||||
const DesktopSize& size() const { return size_; }
|
|
||||||
const DesktopVector& dpi() const { return dpi_; }
|
|
||||||
|
|
||||||
int32_t AddRef() {
|
|
||||||
return ++ref_count_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t Release() {
|
|
||||||
int32_t ref_count;
|
|
||||||
ref_count = --ref_count_;
|
|
||||||
if (ref_count == 0) {
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
return ref_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DesktopRegion& updated_region() {
|
|
||||||
return updated_region_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DesktopRegion& copied_region() {
|
|
||||||
return copied_region_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Texture should only be deleted by Release function.
|
|
||||||
~Texture() = default;
|
|
||||||
|
|
||||||
// Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false
|
|
||||||
// if it fails to execute windows api.
|
|
||||||
bool InitializeStage(ID3D11Texture2D* texture) {
|
|
||||||
RTC_DCHECK(texture);
|
|
||||||
D3D11_TEXTURE2D_DESC desc = {0};
|
|
||||||
texture->GetDesc(&desc);
|
|
||||||
desc.BindFlags = 0;
|
|
||||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
||||||
desc.MiscFlags = 0;
|
|
||||||
desc.Usage = D3D11_USAGE_STAGING;
|
|
||||||
if (stage_) {
|
|
||||||
// Make sure stage_ and surface_ are always pointing to a same object.
|
|
||||||
// We need an ID3D11Texture2D instance for
|
|
||||||
// ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for
|
|
||||||
// IDXGISurface::Map.
|
|
||||||
{
|
|
||||||
ComPtr<IUnknown> left;
|
|
||||||
ComPtr<IUnknown> right;
|
|
||||||
bool left_result = SUCCEEDED(stage_.As(&left));
|
|
||||||
bool right_result = SUCCEEDED(surface_.As(&right));
|
|
||||||
RTC_DCHECK(left_result);
|
|
||||||
RTC_DCHECK(right_result);
|
|
||||||
RTC_DCHECK(left.Get() == right.Get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// This buffer should be used already.
|
|
||||||
_com_error error = _com_error(surface_->Unmap());
|
|
||||||
if (error.Error() == S_OK) {
|
|
||||||
D3D11_TEXTURE2D_DESC current_desc;
|
|
||||||
stage_->GetDesc(¤t_desc);
|
|
||||||
if (memcmp(&desc, ¤t_desc, sizeof(D3D11_TEXTURE2D_DESC)) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Let's recreate stage_ and surface_ later.
|
|
||||||
LOG(LS_ERROR) << "Failed to unmap surface, error "
|
|
||||||
<< error.ErrorMessage() << ", code " << error.Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
stage_.Reset();
|
|
||||||
surface_.Reset();
|
|
||||||
} else {
|
|
||||||
RTC_DCHECK(!surface_);
|
|
||||||
}
|
|
||||||
|
|
||||||
HDC hdc = GetDC(nullptr);
|
|
||||||
// Use old DPI value if failed.
|
|
||||||
if (hdc != nullptr) {
|
|
||||||
dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
|
|
||||||
ReleaseDC(nullptr, hdc);
|
|
||||||
}
|
|
||||||
|
|
||||||
_com_error error = _com_error(g_container->device->CreateTexture2D(
|
|
||||||
&desc, nullptr, stage_.GetAddressOf()));
|
|
||||||
if (error.Error() != S_OK || !stage_) {
|
|
||||||
LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage, "
|
|
||||||
"error "
|
|
||||||
<< error.ErrorMessage() << ", code " << error.Error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = _com_error(stage_.As(&surface_));
|
|
||||||
if (error.Error() != S_OK || !surface_) {
|
|
||||||
LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface, "
|
|
||||||
"error "
|
|
||||||
<< error.ErrorMessage() << ", code " << error.Error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_.set(desc.Width, desc.Height);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<ID3D11Texture2D> stage_;
|
|
||||||
ComPtr<IDXGISurface> surface_;
|
|
||||||
DXGI_MAPPED_RECT rect_;
|
|
||||||
DesktopSize size_;
|
|
||||||
Atomic32 ref_count_;
|
|
||||||
// The updated region from Windows API.
|
|
||||||
DesktopRegion updated_region_;
|
|
||||||
// Combination of updated regions from both current frame and previous frame.
|
|
||||||
DesktopRegion copied_region_;
|
|
||||||
// The DPI of current frame.
|
|
||||||
DesktopVector dpi_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A DesktopFrame which does not own the data buffer, and also does not have
|
|
||||||
// shared memory. This uses in IT2ME scenario only.
|
|
||||||
class ScreenCapturerWinDirectx::DxgiDesktopFrame : public DesktopFrame {
|
|
||||||
public:
|
|
||||||
DxgiDesktopFrame(
|
|
||||||
const rtc::scoped_refptr<ScreenCapturerWinDirectx::Texture>& texture)
|
|
||||||
: DesktopFrame(texture.get()->size(),
|
|
||||||
texture.get()->pitch(),
|
|
||||||
texture.get()->bits(),
|
|
||||||
nullptr),
|
|
||||||
texture_(texture) {
|
|
||||||
set_dpi(texture->dpi());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~DxgiDesktopFrame() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Keep a reference to the Texture instance to make sure we can still access
|
|
||||||
// its bytes array.
|
|
||||||
rtc::scoped_refptr<ScreenCapturerWinDirectx::Texture> texture_;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::Initialize() {
|
|
||||||
if (!g_container) {
|
|
||||||
rtc::GlobalLockScope lock(&g_initialize_lock);
|
|
||||||
if (!g_container) {
|
|
||||||
g_container = new DxgiContainer();
|
|
||||||
g_container->initialize_result = DoInitialize();
|
|
||||||
if (g_container->initialize_result) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up if DirectX cannot work on the system.
|
|
||||||
if (g_container->duplication) {
|
|
||||||
g_container->duplication.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_container->output1) {
|
|
||||||
g_container->output1->Release();
|
|
||||||
g_container->output1 = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_container->context) {
|
|
||||||
g_container->context->Release();
|
|
||||||
g_container->context = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_container->device) {
|
|
||||||
g_container->device->Release();
|
|
||||||
g_container->device = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_container->initialize_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::DoInitialize() {
|
|
||||||
D3D_FEATURE_LEVEL feature_level;
|
|
||||||
_com_error error = D3D11CreateDevice(
|
|
||||||
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
|
|
||||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
|
|
||||||
nullptr, 0, D3D11_SDK_VERSION, &g_container->device, &feature_level,
|
|
||||||
&g_container->context);
|
|
||||||
if (error.Error() != S_OK || !g_container->device || !g_container->context) {
|
|
||||||
LOG(LS_WARNING) << "D3D11CreateDeivce returns error "
|
|
||||||
<< error.ErrorMessage() << " with code " << error.Error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feature_level < D3D_FEATURE_LEVEL_11_0) {
|
|
||||||
LOG(LS_WARNING) << "D3D11CreateDevice returns an instance without DirectX "
|
|
||||||
"11 support, level "
|
|
||||||
<< feature_level;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<IDXGIDevice> device;
|
|
||||||
error = _com_error(g_container->device->QueryInterface(
|
|
||||||
__uuidof(IDXGIDevice), reinterpret_cast<void**>(device.GetAddressOf())));
|
|
||||||
if (error.Error() != S_OK || !device) {
|
|
||||||
LOG(LS_WARNING) << "ID3D11Device is not an implementation of IDXGIDevice, "
|
|
||||||
"this usually means the system does not support DirectX "
|
|
||||||
"11";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<IDXGIAdapter> adapter;
|
|
||||||
error = _com_error(device->GetAdapter(adapter.GetAddressOf()));
|
|
||||||
if (error.Error() != S_OK || !adapter) {
|
|
||||||
LOG(LS_WARNING) << "Failed to get an IDXGIAdapter implementation from "
|
|
||||||
"IDXGIDevice.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<IDXGIOutput> output;
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
error = _com_error(adapter->EnumOutputs(i, output.GetAddressOf()));
|
|
||||||
if (error.Error() == DXGI_ERROR_NOT_FOUND) {
|
|
||||||
LOG(LS_WARNING) << "No output detected.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (error.Error() == S_OK && output) {
|
|
||||||
DXGI_OUTPUT_DESC desc;
|
|
||||||
error = _com_error(output->GetDesc(&desc));
|
|
||||||
if (error.Error() == S_OK) {
|
|
||||||
if (desc.AttachedToDesktop) {
|
|
||||||
// Current output instance is the device attached to desktop.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG(LS_WARNING) << "Failed to get output description of device " << i
|
|
||||||
<< ", ignore.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RTC_DCHECK(output);
|
|
||||||
error = _com_error(output.CopyTo(
|
|
||||||
__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&g_container->output1)));
|
|
||||||
if (error.Error() != S_OK || !g_container->output1) {
|
|
||||||
LOG(LS_WARNING) << "Failed to convert IDXGIOutput to IDXGIOutput1, this "
|
|
||||||
"usually means the system does not support DirectX 11";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When we are initializing the DXGI, retrying several times to avoid any
|
|
||||||
// temporary issue, such as display mode changing, to block us from using
|
|
||||||
// DXGI based capturer.
|
|
||||||
for (int i = 0; i < kDuplicateOutputAttempts; i++) {
|
|
||||||
if (DuplicateOutput()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Sleep(kDuplicateOutputWaitMs);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::DuplicateOutput() {
|
|
||||||
// We are updating the instance.
|
|
||||||
rtc::CritScope lock(&g_container->duplication_lock);
|
|
||||||
// Make sure nobody is using current instance.
|
|
||||||
rtc::CritScope lock2(&g_container->acquire_lock);
|
|
||||||
if (g_container->duplication) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_com_error error = g_container->output1->DuplicateOutput(
|
|
||||||
static_cast<IUnknown*>(g_container->device),
|
|
||||||
g_container->duplication.GetAddressOf());
|
|
||||||
if (error.Error() != S_OK || !g_container->duplication) {
|
|
||||||
g_container->duplication.Reset();
|
|
||||||
LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1, error "
|
|
||||||
<< error.ErrorMessage() << ", with code "
|
|
||||||
<< error.Error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&g_container->duplication_desc, 0, sizeof(DXGI_OUTDUPL_DESC));
|
|
||||||
g_container->duplication->GetDesc(&g_container->duplication_desc);
|
|
||||||
if (g_container->duplication_desc.ModeDesc.Format !=
|
|
||||||
DXGI_FORMAT_B8G8R8A8_UNORM) {
|
|
||||||
g_container->duplication.Reset();
|
|
||||||
LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
|
|
||||||
"format, which is required by downstream components, "
|
|
||||||
"format is "
|
|
||||||
<< g_container->duplication_desc.ModeDesc.Format;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::ForceDuplicateOutput() {
|
|
||||||
// We are updating the instance.
|
|
||||||
rtc::CritScope lock(&g_container->duplication_lock);
|
|
||||||
// Make sure nobody is using current instance.
|
|
||||||
rtc::CritScope lock2(&g_container->acquire_lock);
|
|
||||||
|
|
||||||
if (g_container->duplication) {
|
|
||||||
g_container->duplication->ReleaseFrame();
|
|
||||||
g_container->duplication.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
return DuplicateOutput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenCapturerWinDirectx::ScreenCapturerWinDirectx(
|
ScreenCapturerWinDirectx::ScreenCapturerWinDirectx(
|
||||||
const DesktopCaptureOptions& options)
|
const DesktopCaptureOptions& options)
|
||||||
: callback_(nullptr) {
|
: callback_(nullptr) {}
|
||||||
RTC_DCHECK(g_container && g_container->initialize_result);
|
|
||||||
|
|
||||||
// Texture instance won't change forever.
|
|
||||||
while (!surfaces_.current_frame()) {
|
|
||||||
surfaces_.ReplaceCurrentFrame(std::unique_ptr<rtc::scoped_refptr<Texture>>(
|
|
||||||
new rtc::scoped_refptr<Texture>(new Texture())));
|
|
||||||
surfaces_.MoveToNextFrame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() {}
|
ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() {}
|
||||||
|
|
||||||
@ -477,202 +44,65 @@ void ScreenCapturerWinDirectx::SetSharedMemoryFactory(
|
|||||||
shared_memory_factory_ = std::move(shared_memory_factory);
|
shared_memory_factory_ = std::move(shared_memory_factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::HandleDetectUpdatedRegionError(
|
DesktopSize ScreenCapturerWinDirectx::SelectedDesktopSize() const {
|
||||||
const _com_error& error,
|
if (current_screen_id == kFullDesktopScreenId) {
|
||||||
const char* stage) {
|
return DxgiDuplicatorController::Instance()->desktop_size();
|
||||||
if (error.Error() != S_OK) {
|
|
||||||
if (error.Error() == DXGI_ERROR_ACCESS_LOST) {
|
|
||||||
ForceDuplicateOutput();
|
|
||||||
} else {
|
|
||||||
LOG(LS_ERROR) << "Failed to get " << stage << " rectangles, error "
|
|
||||||
<< error.ErrorMessage() << ", code " << error.Error();
|
|
||||||
}
|
|
||||||
// Send entire desktop as we cannot get dirty or move rectangles.
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return DxgiDuplicatorController::Instance()
|
||||||
return true;
|
->ScreenRect(current_screen_id)
|
||||||
}
|
.size();
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::DetectUpdatedRegion(
|
|
||||||
const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
|
||||||
DesktopRegion* updated_region) {
|
|
||||||
RTC_DCHECK(g_container->duplication);
|
|
||||||
RTC_DCHECK(updated_region);
|
|
||||||
updated_region->Clear();
|
|
||||||
if (frame_info.TotalMetadataBufferSize == 0) {
|
|
||||||
// This should not happen, since frame_info.AccumulatedFrames > 0.
|
|
||||||
LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
|
|
||||||
"but TotalMetadataBufferSize == 0";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_container->metadata.capacity() < frame_info.TotalMetadataBufferSize) {
|
|
||||||
g_container->metadata.clear(); // Avoid data copy
|
|
||||||
g_container->metadata.reserve(frame_info.TotalMetadataBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT buff_size = 0;
|
|
||||||
DXGI_OUTDUPL_MOVE_RECT* move_rects =
|
|
||||||
reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(g_container->metadata.data());
|
|
||||||
size_t move_rects_count = 0;
|
|
||||||
_com_error error = _com_error(g_container->duplication->GetFrameMoveRects(
|
|
||||||
static_cast<UINT>(g_container->metadata.capacity()),
|
|
||||||
move_rects, &buff_size));
|
|
||||||
if (!HandleDetectUpdatedRegionError(error, "move")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
|
|
||||||
|
|
||||||
RECT* dirty_rects =
|
|
||||||
reinterpret_cast<RECT*>(g_container->metadata.data() + buff_size);
|
|
||||||
size_t dirty_rects_count = 0;
|
|
||||||
error = _com_error(g_container->duplication->GetFrameDirtyRects(
|
|
||||||
static_cast<UINT>(g_container->metadata.capacity()) - buff_size,
|
|
||||||
dirty_rects, &buff_size));
|
|
||||||
if (!HandleDetectUpdatedRegionError(error, "dirty")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dirty_rects_count = buff_size / sizeof(RECT);
|
|
||||||
|
|
||||||
while (move_rects_count > 0) {
|
|
||||||
updated_region->AddRect(DesktopRect::MakeXYWH(
|
|
||||||
move_rects->SourcePoint.x, move_rects->SourcePoint.y,
|
|
||||||
move_rects->DestinationRect.right - move_rects->DestinationRect.left,
|
|
||||||
move_rects->DestinationRect.bottom - move_rects->DestinationRect.top));
|
|
||||||
updated_region->AddRect(DesktopRect::MakeLTRB(
|
|
||||||
move_rects->DestinationRect.left, move_rects->DestinationRect.top,
|
|
||||||
move_rects->DestinationRect.right, move_rects->DestinationRect.bottom));
|
|
||||||
move_rects++;
|
|
||||||
move_rects_count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (dirty_rects_count > 0) {
|
|
||||||
updated_region->AddRect(DesktopRect::MakeLTRB(
|
|
||||||
dirty_rects->left, dirty_rects->top,
|
|
||||||
dirty_rects->right, dirty_rects->bottom));
|
|
||||||
dirty_rects++;
|
|
||||||
dirty_rects_count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<DesktopFrame> ScreenCapturerWinDirectx::ProcessFrame(
|
|
||||||
const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
|
||||||
IDXGIResource* resource) {
|
|
||||||
RTC_DCHECK(resource);
|
|
||||||
RTC_DCHECK(frame_info.AccumulatedFrames > 0);
|
|
||||||
// We have something to update, so move to next surface.
|
|
||||||
surfaces_.MoveToNextFrame();
|
|
||||||
if (shared_memory_factory_) {
|
|
||||||
// Make sure frames_ and surfaces_ are synchronized if we are using both.
|
|
||||||
frames_.MoveToNextFrame();
|
|
||||||
}
|
|
||||||
RTC_DCHECK(surfaces_.current_frame());
|
|
||||||
if (!surfaces_.current_frame()->get()->CopyFrom(frame_info, resource,
|
|
||||||
surfaces_.previous_frame()->get()->updated_region())) {
|
|
||||||
return std::unique_ptr<DesktopFrame>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<DesktopFrame> result;
|
|
||||||
if (shared_memory_factory_) {
|
|
||||||
// When using shared memory, |frames_| is used to store a queue of
|
|
||||||
// SharedMemoryDesktopFrame's.
|
|
||||||
if (!frames_.current_frame() ||
|
|
||||||
!frames_.current_frame()->size().equals(
|
|
||||||
surfaces_.current_frame()->get()->size())) {
|
|
||||||
// Current frame does not have a same size as last captured surface.
|
|
||||||
std::unique_ptr<DesktopFrame> new_frame =
|
|
||||||
SharedMemoryDesktopFrame::Create(
|
|
||||||
surfaces_.current_frame()->get()->size(),
|
|
||||||
shared_memory_factory_.get());
|
|
||||||
if (!new_frame) {
|
|
||||||
LOG(LS_ERROR) << "Failed to allocate a new SharedMemoryDesktopFrame";
|
|
||||||
return std::unique_ptr<DesktopFrame>();
|
|
||||||
}
|
|
||||||
frames_.ReplaceCurrentFrame(
|
|
||||||
SharedDesktopFrame::Wrap(std::move(new_frame)));
|
|
||||||
}
|
|
||||||
result = frames_.current_frame()->Share();
|
|
||||||
|
|
||||||
std::unique_ptr<DesktopFrame> frame(
|
|
||||||
new DxgiDesktopFrame(*surfaces_.current_frame()));
|
|
||||||
// Copy data into SharedMemory.
|
|
||||||
for (DesktopRegion::Iterator it(
|
|
||||||
surfaces_.current_frame()->get()->copied_region());
|
|
||||||
!it.IsAtEnd();
|
|
||||||
it.Advance()) {
|
|
||||||
result->CopyPixelsFrom(*frame, it.rect().top_left(), it.rect());
|
|
||||||
}
|
|
||||||
result->set_dpi(frame->dpi());
|
|
||||||
} else {
|
|
||||||
result.reset(new DxgiDesktopFrame(*surfaces_.current_frame()));
|
|
||||||
}
|
|
||||||
RTC_DCHECK(result);
|
|
||||||
*result->mutable_updated_region() =
|
|
||||||
surfaces_.current_frame()->get()->updated_region();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) {
|
void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) {
|
||||||
RTC_DCHECK(callback_);
|
RTC_DCHECK(callback_);
|
||||||
|
|
||||||
if (!g_container->duplication && !DuplicateOutput()) {
|
|
||||||
// Failed to initialize desktop duplication. This usually happens when
|
|
||||||
// Windows is switching display mode. Retrying later usually resolves the
|
|
||||||
// issue.
|
|
||||||
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RTC_DCHECK(g_container->duplication);
|
|
||||||
int64_t capture_start_time_nanos = rtc::TimeNanos();
|
int64_t capture_start_time_nanos = rtc::TimeNanos();
|
||||||
|
|
||||||
DXGI_OUTDUPL_FRAME_INFO frame_info;
|
frames_.MoveToNextFrame();
|
||||||
memset(&frame_info, 0, sizeof(DXGI_OUTDUPL_FRAME_INFO));
|
if (!frames_.current_frame()) {
|
||||||
ComPtr<IDXGIResource> resource;
|
std::unique_ptr<DesktopFrame> new_frame;
|
||||||
rtc::CritScope lock(&g_container->acquire_lock);
|
if (shared_memory_factory_) {
|
||||||
_com_error error = g_container->duplication->AcquireNextFrame(
|
new_frame = SharedMemoryDesktopFrame::Create(
|
||||||
kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
|
SelectedDesktopSize(), shared_memory_factory_.get());
|
||||||
|
|
||||||
if (error.Error() == DXGI_ERROR_WAIT_TIMEOUT) {
|
|
||||||
// Nothing changed.
|
|
||||||
EmitCurrentFrame();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.Error() != S_OK) {
|
|
||||||
LOG(LS_ERROR) << "Failed to capture frame, error " << error.ErrorMessage()
|
|
||||||
<< ", code " << error.Error();
|
|
||||||
if (ForceDuplicateOutput()) {
|
|
||||||
EmitCurrentFrame();
|
|
||||||
} else {
|
} else {
|
||||||
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
new_frame.reset(new BasicDesktopFrame(SelectedDesktopSize()));
|
||||||
}
|
}
|
||||||
return;
|
if (!new_frame) {
|
||||||
|
LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame.";
|
||||||
|
// This usually means we do not have enough memory or SharedMemoryFactory
|
||||||
|
// cannot work correctly.
|
||||||
|
callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frames_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(new_frame)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_info.AccumulatedFrames == 0) {
|
if (current_screen_id == kFullDesktopScreenId) {
|
||||||
g_container->duplication->ReleaseFrame();
|
if (!DxgiDuplicatorController::Instance()->Duplicate(
|
||||||
EmitCurrentFrame();
|
&context_, frames_.previous_frame(), frames_.current_frame())) {
|
||||||
return;
|
// Screen size may be changed, so we need to reset the frames.
|
||||||
}
|
frames_.Reset();
|
||||||
|
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
||||||
// Everything looks good so far, build next frame.
|
return;
|
||||||
std::unique_ptr<DesktopFrame> result =
|
}
|
||||||
ProcessFrame(frame_info, resource.Get());
|
} else {
|
||||||
// DetectUpdatedRegion may release last g_container->duplication. But
|
if (!DxgiDuplicatorController::Instance()->DuplicateMonitor(
|
||||||
// ForctDuplicateOutput function will always release last frame, so there is
|
&context_, current_screen_id, frames_.previous_frame(),
|
||||||
// no potential leak.
|
frames_.current_frame())) {
|
||||||
if (g_container->duplication) {
|
// Screen size may be changed, so we need to reset the frames.
|
||||||
g_container->duplication->ReleaseFrame();
|
frames_.Reset();
|
||||||
}
|
if (current_screen_id >=
|
||||||
if (!result) {
|
DxgiDuplicatorController::Instance()->ScreenCount()) {
|
||||||
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
// Current monitor has been removed from the system.
|
||||||
return;
|
callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
|
||||||
|
} else {
|
||||||
|
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<DesktopFrame> result = frames_.current_frame()->Share();
|
||||||
result->set_capture_time_ms(
|
result->set_capture_time_ms(
|
||||||
(rtc::TimeNanos() - capture_start_time_nanos) /
|
(rtc::TimeNanos() - capture_start_time_nanos) /
|
||||||
rtc::kNumNanosecsPerMillisec);
|
rtc::kNumNanosecsPerMillisec);
|
||||||
@ -680,41 +110,31 @@ void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::GetScreenList(ScreenList* screens) {
|
bool ScreenCapturerWinDirectx::GetScreenList(ScreenList* screens) {
|
||||||
|
int screen_count = DxgiDuplicatorController::Instance()->ScreenCount();
|
||||||
|
for (int i = 0; i < screen_count; i++) {
|
||||||
|
screens->push_back(Screen{i});
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScreenCapturerWinDirectx::SelectScreen(ScreenId id) {
|
bool ScreenCapturerWinDirectx::SelectScreen(ScreenId id) {
|
||||||
// Only full desktop capture is supported.
|
if (id == current_screen_id) {
|
||||||
return id == kFullDesktopScreenId;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
void ScreenCapturerWinDirectx::EmitCurrentFrame() {
|
|
||||||
if (!surfaces_.current_frame()->get()->bits()) {
|
|
||||||
// At the very begining, we have not captured any frames.
|
|
||||||
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shared_memory_factory_) {
|
// Changing target screen may or may not impact frame size. So resetting
|
||||||
// If shared_memory_factory_ is provided, last frame is stored in frames_
|
// frames only when a Duplicate() function call returns false.
|
||||||
// queue. If there is not an existing frame (at the very begining), we can
|
if (id == kFullDesktopScreenId) {
|
||||||
// only return a nullptr.
|
current_screen_id = id;
|
||||||
if (frames_.current_frame()) {
|
return true;
|
||||||
std::unique_ptr<SharedDesktopFrame> frame =
|
|
||||||
frames_.current_frame()->Share();
|
|
||||||
frame->mutable_updated_region()->Clear();
|
|
||||||
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
|
|
||||||
} else {
|
|
||||||
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no shared_memory_factory_, last frame is stored in surfaces_
|
int screen_count = DxgiDuplicatorController::Instance()->ScreenCount();
|
||||||
// queue.
|
if (id >= 0 && id < screen_count) {
|
||||||
std::unique_ptr<DesktopFrame> frame(
|
current_screen_id = id;
|
||||||
new DxgiDesktopFrame(*surfaces_.current_frame()));
|
return true;
|
||||||
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -13,23 +13,14 @@
|
|||||||
|
|
||||||
#include "webrtc/modules/desktop_capture/screen_capturer.h"
|
#include "webrtc/modules/desktop_capture/screen_capturer.h"
|
||||||
|
|
||||||
#include <comdef.h>
|
|
||||||
#include <D3DCommon.h>
|
|
||||||
#include <D3D11.h>
|
|
||||||
#include <DXGI.h>
|
|
||||||
#include <DXGI1_2.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <wrl/client.h>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webrtc/base/thread_annotations.h"
|
|
||||||
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
|
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
|
|
||||||
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
#include "webrtc/modules/desktop_capture/desktop_region.h"
|
||||||
#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
|
#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
|
||||||
#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
|
#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
|
||||||
|
#include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -37,10 +28,8 @@ namespace webrtc {
|
|||||||
// implementation won't work when ScreenCaptureFrameQueue.kQueueLength is not 2.
|
// implementation won't work when ScreenCaptureFrameQueue.kQueueLength is not 2.
|
||||||
class ScreenCapturerWinDirectx : public ScreenCapturer {
|
class ScreenCapturerWinDirectx : public ScreenCapturer {
|
||||||
public:
|
public:
|
||||||
// Initializes DirectX related components. Returns false if any error
|
// Whether the system support DirectX based capturing.
|
||||||
// happened, any instance of this class won't be able to work in such status.
|
static bool IsSupported();
|
||||||
// Thread safe, guarded by initialize_lock.
|
|
||||||
static bool Initialize();
|
|
||||||
|
|
||||||
explicit ScreenCapturerWinDirectx(const DesktopCaptureOptions& options);
|
explicit ScreenCapturerWinDirectx(const DesktopCaptureOptions& options);
|
||||||
virtual ~ScreenCapturerWinDirectx();
|
virtual ~ScreenCapturerWinDirectx();
|
||||||
@ -53,48 +42,17 @@ class ScreenCapturerWinDirectx : public ScreenCapturer {
|
|||||||
bool SelectScreen(ScreenId id) override;
|
bool SelectScreen(ScreenId id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Texture is a pair of an ID3D11Texture2D and an IDXGISurface. Refer to its
|
// Returns desktop size of selected screen.
|
||||||
// implementation in source code for details.
|
DesktopSize SelectedDesktopSize() const;
|
||||||
class Texture;
|
|
||||||
|
|
||||||
// An implementation of DesktopFrame to return data from a Texture instance.
|
|
||||||
class DxgiDesktopFrame;
|
|
||||||
|
|
||||||
static bool DoInitialize();
|
|
||||||
|
|
||||||
// Initializes DxgiOutputDuplication. If current DxgiOutputDuplication
|
|
||||||
// instance is existing, this function takes no-op and returns true. Returns
|
|
||||||
// false if it fails to execute windows api.
|
|
||||||
static bool DuplicateOutput();
|
|
||||||
|
|
||||||
// Deprecates current DxgiOutputDuplication instance and calls DuplicateOutput
|
|
||||||
// to reinitialize it.
|
|
||||||
static bool ForceDuplicateOutput();
|
|
||||||
|
|
||||||
// Detects update regions in last frame, if anything wrong, returns false.
|
|
||||||
// ProcessFrame will insert a whole desktop size as updated region instead.
|
|
||||||
static bool DetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
|
||||||
DesktopRegion* updated_region);
|
|
||||||
|
|
||||||
// A helper function to handle _com_error result in DetectUpdatedRegion.
|
|
||||||
// Returns false if the _com_error shows an error.
|
|
||||||
static bool HandleDetectUpdatedRegionError(const _com_error& error,
|
|
||||||
const char* stage);
|
|
||||||
|
|
||||||
// Processes one frame received from AcquireNextFrame function, returns a
|
|
||||||
// nullptr if anything wrong.
|
|
||||||
std::unique_ptr<DesktopFrame> ProcessFrame(
|
|
||||||
const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
|
||||||
IDXGIResource* resource);
|
|
||||||
|
|
||||||
// A shortcut to execute callback with current frame in frames.
|
|
||||||
void EmitCurrentFrame();
|
|
||||||
|
|
||||||
ScreenCaptureFrameQueue<rtc::scoped_refptr<Texture>> surfaces_;
|
|
||||||
ScreenCaptureFrameQueue<SharedDesktopFrame> frames_;
|
ScreenCaptureFrameQueue<SharedDesktopFrame> frames_;
|
||||||
std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
|
std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
|
||||||
Callback* callback_ = nullptr;
|
Callback* callback_ = nullptr;
|
||||||
|
|
||||||
|
DxgiDuplicatorController::Context context_;
|
||||||
|
|
||||||
|
ScreenId current_screen_id = kFullDesktopScreenId;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinDirectx);
|
RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinDirectx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user