wgc dll test pass

This commit is contained in:
dijunkun
2023-08-28 17:19:46 +08:00
parent e970bdc929
commit d4b1ac1fb8
35 changed files with 1745 additions and 50 deletions

View File

@@ -97,9 +97,9 @@ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance,
WINRT_VERIFY(comboBoxHwnd);
// Populate combo box
for (auto &window : g_windows) {
SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, (LPARAM)window.Title().c_str());
}
// for (auto &window : g_windows) {
// SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, (LPARAM)window.Title().c_str());
// }
for (auto &monitor : g_monitors) {
SendMessage(comboBoxHwnd, CB_ADDSTRING, 0,
@@ -141,13 +141,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
case WM_COMMAND:
if (HIWORD(wParam) == CBN_SELCHANGE) {
auto index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
if (index < g_windows.size() - 1) {
auto window = g_windows[index];
g_app->StartCapture(window.Hwnd());
} else {
auto monitor = g_monitors[index - g_windows.size()];
// if (index < g_windows.size() - 1) {
// auto window = g_windows[index];
// g_app->StartCapture(window.Hwnd());
// } else {
// auto monitor = g_monitors[index - g_windows.size()];
auto monitor = g_monitors[0];
g_app->StartCapture(monitor.Hmonitor());
}
// }
}
break;
default:

View File

34
dll/main.cpp Normal file
View File

@@ -0,0 +1,34 @@
#define AMRECORDER_IMPORT
#include <iostream>
#include "export.h"
#include "head.h"
#include "record_desktop_wgc.h"
int main() {
// bool is_supported = wgc_is_supported();
// if (!wgc_is_supported) {
// std::cout << "Not support wgc" << std::endl;
// return -1;
// }
static am::record_desktop *recorder = new am::record_desktop_wgc();
RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = GetSystemMetrics(SM_CXSCREEN);
rect.bottom = GetSystemMetrics(SM_CYSCREEN);
recorder->init(rect, 10);
recorder->start();
// int pause() override;
// int resume() override;
// int stop() override;
while(1){}
return 0;
}

View File

View File

@@ -1,18 +1,21 @@
#include "record_desktop_wgc.h"
#include "utils_string.h"
#include "system_error.h"
#include "error_define.h"
#include "log_helper.h"
#include "system_error.h"
#include "utils_string.h"
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
LPARAM data) {
MONITORINFOEX info_ex;
info_ex.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hmonitor, &info_ex);
if (info_ex.dwFlags == DISPLAY_DEVICE_MIRRORING_DRIVER) return true;
if (info_ex.dwFlags == DISPLAY_DEVICE_MIRRORING_DRIVER)
return true;
if (info_ex.dwFlags & MONITORINFOF_PRIMARY) {
*(HMONITOR *)data = hmonitor;
@@ -31,6 +34,7 @@ HMONITOR GetPrimaryMonitor() {
namespace am {
record_desktop_wgc::record_desktop_wgc() {}
record_desktop_wgc::~record_desktop_wgc() {
@@ -40,7 +44,8 @@ record_desktop_wgc::~record_desktop_wgc() {
int record_desktop_wgc::init(const RECORD_DESKTOP_RECT &rect, const int fps) {
int error = AE_NO;
if (_inited == true) return error;
if (_inited == true)
return error;
_fps = fps;
_rect = rect;
@@ -49,12 +54,12 @@ int record_desktop_wgc::init(const RECORD_DESKTOP_RECT &rect, const int fps) {
_pixel_fmt = AV_PIX_FMT_BGRA;
do {
if (!module_.is_supported()) {
if (!wgc_is_supported()) {
error = AE_UNSUPPORT;
break;
}
session_ = module_.create_session();
session_ = wgc_create_session();
if (!session_) {
error = AE_WGC_CREATE_CAPTURER_FAILED;
break;
@@ -93,20 +98,23 @@ int record_desktop_wgc::start() {
int record_desktop_wgc::pause() {
_paused = true;
if (session_) session_->pause();
if (session_)
session_->pause();
return AE_NO;
}
int record_desktop_wgc::resume() {
_paused = false;
if (session_) session_->resume();
if (session_)
session_->resume();
return AE_NO;
}
int record_desktop_wgc::stop() {
_running = false;
if (session_) session_->stop();
if (session_)
session_->stop();
return AE_NO;
}
@@ -128,7 +136,8 @@ void record_desktop_wgc::on_frame(const wgc_session::wgc_session_frame &frame) {
av_image_fill_arrays(av_frame->data, av_frame->linesize, frame.data,
AV_PIX_FMT_BGRA, frame.width, frame.height, 1);
if (_on_data) _on_data(av_frame);
if (_on_data)
_on_data(av_frame);
av_frame_free(&av_frame);
}
@@ -136,7 +145,8 @@ void record_desktop_wgc::on_frame(const wgc_session::wgc_session_frame &frame) {
void record_desktop_wgc::clean_up() {
_inited = false;
if (session_) session_->release();
if (session_)
session_->release();
session_ = nullptr;
}

View File

@@ -1,17 +0,0 @@
#define AMRECORDER_IMPORT
#include <iostream>
#include "export.h"
#include "head.h"
int main() {
bool is_supported = wgc_is_supported();
if (!wgc_is_supported) {
std::cout << "Not support wgc" << std::endl;
return -1;
}
rd rd;
return 0;
}

View File

@@ -0,0 +1,372 @@
/*
* Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capture_session.h"
#include <windows.graphics.capture.interop.h>
#include <windows.graphics.directX.direct3d11.interop.h>
#include <wrl.h>
#include <memory>
#include <utility>
#include <vector>
#include "modules/desktop_capture/win/wgc_desktop_frame.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/win/create_direct3d_device.h"
#include "rtc_base/win/get_activation_factory.h"
#include "system_wrappers/include/metrics.h"
using Microsoft::WRL::ComPtr;
namespace WGC = ABI::Windows::Graphics::Capture;
namespace webrtc {
namespace {
// We must use a BGRA pixel format that has 4 bytes per pixel, as required by
// the DesktopFrame interface.
const auto kPixelFormat = ABI::Windows::Graphics::DirectX::DirectXPixelFormat::
DirectXPixelFormat_B8G8R8A8UIntNormalized;
// We only want 1 buffer in our frame pool to reduce latency. If we had more,
// they would sit in the pool for longer and be stale by the time we are asked
// for a new frame.
const int kNumBuffers = 1;
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class StartCaptureResult {
kSuccess = 0,
kSourceClosed = 1,
kAddClosedFailed = 2,
kDxgiDeviceCastFailed = 3,
kD3dDelayLoadFailed = 4,
kD3dDeviceCreationFailed = 5,
kFramePoolActivationFailed = 6,
kFramePoolCastFailed = 7,
kGetItemSizeFailed = 8,
kCreateFreeThreadedFailed = 9,
kCreateCaptureSessionFailed = 10,
kStartCaptureFailed = 11,
kMaxValue = kStartCaptureFailed
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class GetFrameResult {
kSuccess = 0,
kItemClosed = 1,
kTryGetNextFrameFailed = 2,
kFrameDropped = 3,
kGetSurfaceFailed = 4,
kDxgiInterfaceAccessFailed = 5,
kTexture2dCastFailed = 6,
kCreateMappedTextureFailed = 7,
kMapFrameFailed = 8,
kGetContentSizeFailed = 9,
kResizeMappedTextureFailed = 10,
kRecreateFramePoolFailed = 11,
kMaxValue = kRecreateFramePoolFailed
};
void RecordStartCaptureResult(StartCaptureResult error) {
RTC_HISTOGRAM_ENUMERATION(
"WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult",
static_cast<int>(error), static_cast<int>(StartCaptureResult::kMaxValue));
}
void RecordGetFrameResult(GetFrameResult error) {
RTC_HISTOGRAM_ENUMERATION(
"WebRTC.DesktopCapture.Win.WgcCaptureSessionGetFrameResult",
static_cast<int>(error), static_cast<int>(GetFrameResult::kMaxValue));
}
} // namespace
WgcCaptureSession::WgcCaptureSession(ComPtr<ID3D11Device> d3d11_device,
ComPtr<WGC::IGraphicsCaptureItem> item)
: d3d11_device_(std::move(d3d11_device)), item_(std::move(item)) {}
WgcCaptureSession::~WgcCaptureSession() = default;
HRESULT WgcCaptureSession::StartCapture() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_DCHECK(!is_capture_started_);
if (item_closed_) {
RTC_LOG(LS_ERROR) << "The target source has been closed.";
RecordStartCaptureResult(StartCaptureResult::kSourceClosed);
return E_ABORT;
}
RTC_DCHECK(d3d11_device_);
RTC_DCHECK(item_);
// Listen for the Closed event, to detect if the source we are capturing is
// closed (e.g. application window is closed or monitor is disconnected). If
// it is, we should abort the capture.
auto closed_handler =
Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
WGC::GraphicsCaptureItem*, IInspectable*>>(
this, &WgcCaptureSession::OnItemClosed);
EventRegistrationToken item_closed_token;
HRESULT hr = item_->add_Closed(closed_handler.Get(), &item_closed_token);
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kAddClosedFailed);
return hr;
}
ComPtr<IDXGIDevice> dxgi_device;
hr = d3d11_device_->QueryInterface(IID_PPV_ARGS(&dxgi_device));
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kDxgiDeviceCastFailed);
return hr;
}
if (!ResolveCoreWinRTDirect3DDelayload()) {
RecordStartCaptureResult(StartCaptureResult::kD3dDelayLoadFailed);
return E_FAIL;
}
hr = CreateDirect3DDeviceFromDXGIDevice(dxgi_device.Get(), &direct3d_device_);
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kD3dDeviceCreationFailed);
return hr;
}
ComPtr<WGC::IDirect3D11CaptureFramePoolStatics> frame_pool_statics;
hr = GetActivationFactory<
ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics,
RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>(
&frame_pool_statics);
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kFramePoolActivationFailed);
return hr;
}
// Cast to FramePoolStatics2 so we can use CreateFreeThreaded and avoid the
// need to have a DispatcherQueue. We don't listen for the FrameArrived event,
// so there's no difference.
ComPtr<WGC::IDirect3D11CaptureFramePoolStatics2> frame_pool_statics2;
hr = frame_pool_statics->QueryInterface(IID_PPV_ARGS(&frame_pool_statics2));
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kFramePoolCastFailed);
return hr;
}
ABI::Windows::Graphics::SizeInt32 item_size;
hr = item_.Get()->get_Size(&item_size);
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kGetItemSizeFailed);
return hr;
}
previous_size_ = item_size;
hr = frame_pool_statics2->CreateFreeThreaded(direct3d_device_.Get(),
kPixelFormat, kNumBuffers,
item_size, &frame_pool_);
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kCreateFreeThreadedFailed);
return hr;
}
hr = frame_pool_->CreateCaptureSession(item_.Get(), &session_);
if (FAILED(hr)) {
RecordStartCaptureResult(StartCaptureResult::kCreateCaptureSessionFailed);
return hr;
}
hr = session_->StartCapture();
if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr;
RecordStartCaptureResult(StartCaptureResult::kStartCaptureFailed);
return hr;
}
RecordStartCaptureResult(StartCaptureResult::kSuccess);
is_capture_started_ = true;
return hr;
}
HRESULT WgcCaptureSession::GetFrame(
std::unique_ptr<DesktopFrame>* output_frame) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
if (item_closed_) {
RTC_LOG(LS_ERROR) << "The target source has been closed.";
RecordGetFrameResult(GetFrameResult::kItemClosed);
return E_ABORT;
}
RTC_DCHECK(is_capture_started_);
ComPtr<WGC::IDirect3D11CaptureFrame> capture_frame;
HRESULT hr = frame_pool_->TryGetNextFrame(&capture_frame);
if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "TryGetNextFrame failed: " << hr;
RecordGetFrameResult(GetFrameResult::kTryGetNextFrameFailed);
return hr;
}
if (!capture_frame) {
RecordGetFrameResult(GetFrameResult::kFrameDropped);
return hr;
}
// We need to get this CaptureFrame as an ID3D11Texture2D so that we can get
// the raw image data in the format required by the DesktopFrame interface.
ComPtr<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>
d3d_surface;
hr = capture_frame->get_Surface(&d3d_surface);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kGetSurfaceFailed);
return hr;
}
ComPtr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>
direct3DDxgiInterfaceAccess;
hr = d3d_surface->QueryInterface(IID_PPV_ARGS(&direct3DDxgiInterfaceAccess));
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kDxgiInterfaceAccessFailed);
return hr;
}
ComPtr<ID3D11Texture2D> texture_2D;
hr = direct3DDxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&texture_2D));
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kTexture2dCastFailed);
return hr;
}
if (!mapped_texture_) {
hr = CreateMappedTexture(texture_2D);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed);
return hr;
}
}
// We need to copy |texture_2D| into |mapped_texture_| as the latter has the
// D3D11_CPU_ACCESS_READ flag set, which lets us access the image data.
// Otherwise it would only be readable by the GPU.
ComPtr<ID3D11DeviceContext> d3d_context;
d3d11_device_->GetImmediateContext(&d3d_context);
d3d_context->CopyResource(mapped_texture_.Get(), texture_2D.Get());
D3D11_MAPPED_SUBRESOURCE map_info;
hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0,
D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0,
&map_info);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kMapFrameFailed);
return hr;
}
ABI::Windows::Graphics::SizeInt32 new_size;
hr = capture_frame->get_ContentSize(&new_size);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kGetContentSizeFailed);
return hr;
}
// If the size has changed since the last capture, we must be sure to use
// the smaller dimensions. Otherwise we might overrun our buffer, or
// read stale data from the last frame.
int image_height = std::min(previous_size_.Height, new_size.Height);
int image_width = std::min(previous_size_.Width, new_size.Width);
int row_data_length = image_width * DesktopFrame::kBytesPerPixel;
// Make a copy of the data pointed to by |map_info.pData| so we are free to
// unmap our texture.
uint8_t* src_data = static_cast<uint8_t*>(map_info.pData);
std::vector<uint8_t> image_data;
image_data.reserve(image_height * row_data_length);
uint8_t* image_data_ptr = image_data.data();
for (int i = 0; i < image_height; i++) {
memcpy(image_data_ptr, src_data, row_data_length);
image_data_ptr += row_data_length;
src_data += map_info.RowPitch;
}
// Transfer ownership of |image_data| to the output_frame.
DesktopSize size(image_width, image_height);
*output_frame = std::make_unique<WgcDesktopFrame>(size, row_data_length,
std::move(image_data));
d3d_context->Unmap(mapped_texture_.Get(), 0);
// If the size changed, we must resize the texture and frame pool to fit the
// new size.
if (previous_size_.Height != new_size.Height ||
previous_size_.Width != new_size.Width) {
hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed);
return hr;
}
hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat,
kNumBuffers, new_size);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed);
return hr;
}
}
RecordGetFrameResult(GetFrameResult::kSuccess);
previous_size_ = new_size;
return hr;
}
HRESULT WgcCaptureSession::CreateMappedTexture(
ComPtr<ID3D11Texture2D> src_texture,
UINT width,
UINT height) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
D3D11_TEXTURE2D_DESC src_desc;
src_texture->GetDesc(&src_desc);
D3D11_TEXTURE2D_DESC map_desc;
map_desc.Width = width == 0 ? src_desc.Width : width;
map_desc.Height = height == 0 ? src_desc.Height : height;
map_desc.MipLevels = src_desc.MipLevels;
map_desc.ArraySize = src_desc.ArraySize;
map_desc.Format = src_desc.Format;
map_desc.SampleDesc = src_desc.SampleDesc;
map_desc.Usage = D3D11_USAGE_STAGING;
map_desc.BindFlags = 0;
map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
map_desc.MiscFlags = 0;
return d3d11_device_->CreateTexture2D(&map_desc, nullptr, &mapped_texture_);
}
HRESULT WgcCaptureSession::OnItemClosed(WGC::IGraphicsCaptureItem* sender,
IInspectable* event_args) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_LOG(LS_INFO) << "Capture target has been closed.";
item_closed_ = true;
is_capture_started_ = false;
mapped_texture_ = nullptr;
session_ = nullptr;
frame_pool_ = nullptr;
direct3d_device_ = nullptr;
item_ = nullptr;
d3d11_device_ = nullptr;
return S_OK;
}
} // namespace webrtc

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
#include <d3d11.h>
#include <windows.graphics.capture.h>
#include <wrl/client.h>
#include <memory>
#include "api/sequence_checker.h"
#include "modules/desktop_capture/desktop_capture_options.h"
#include "modules/desktop_capture/win/wgc_capture_source.h"
namespace webrtc {
class WgcCaptureSession final {
public:
WgcCaptureSession(
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item);
// Disallow copy and assign.
WgcCaptureSession(const WgcCaptureSession&) = delete;
WgcCaptureSession& operator=(const WgcCaptureSession&) = delete;
~WgcCaptureSession();
HRESULT StartCapture();
// Returns a frame from the frame pool, if any are present.
HRESULT GetFrame(std::unique_ptr<DesktopFrame>* output_frame);
bool IsCaptureStarted() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return is_capture_started_;
}
private:
// Initializes |mapped_texture_| with the properties of the |src_texture|,
// overrides the values of some necessary properties like the
// D3D11_CPU_ACCESS_READ flag. Also has optional parameters for what size
// |mapped_texture_| should be, if they aren't provided we will use the size
// of |src_texture|.
HRESULT CreateMappedTexture(
Microsoft::WRL::ComPtr<ID3D11Texture2D> src_texture,
UINT width = 0,
UINT height = 0);
// Event handler for |item_|'s Closed event.
HRESULT OnItemClosed(
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* sender,
IInspectable* event_args);
// A Direct3D11 Device provided by the caller. We use this to create an
// IDirect3DDevice, and also to create textures that will hold the image data.
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
// This item represents what we are capturing, we use it to create the
// capture session, and also to listen for the Closed event.
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
item_;
// The IDirect3DDevice is necessary to instantiate the frame pool.
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>
direct3d_device_;
// The frame pool is where frames are deposited during capture, we retrieve
// them from here with TryGetNextFrame().
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
frame_pool_;
// This texture holds the final image data. We made it a member so we can
// reuse it, instead of having to create a new texture every time we grab a
// frame.
Microsoft::WRL::ComPtr<ID3D11Texture2D> mapped_texture_;
// This lets us know when the source has been resized, which is important
// because we must resize the framepool and our texture to be able to hold
// enough data for the frame.
ABI::Windows::Graphics::SizeInt32 previous_size_;
// The capture session lets us set properties about the capture before it
// starts such as whether to capture the mouse cursor, and it lets us tell WGC
// to start capturing frames.
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureSession>
session_;
bool item_closed_ = false;
bool is_capture_started_ = false;
SequenceChecker sequence_checker_;
};
} // namespace webrtc
#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_

View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capture_source.h"
#include <windows.graphics.capture.interop.h>
#include <windows.h>
#include <utility>
#include "modules/desktop_capture/win/screen_capture_utils.h"
#include "modules/desktop_capture/win/window_capture_utils.h"
#include "rtc_base/win/get_activation_factory.h"
using Microsoft::WRL::ComPtr;
namespace WGC = ABI::Windows::Graphics::Capture;
namespace webrtc {
WgcCaptureSource::WgcCaptureSource(DesktopCapturer::SourceId source_id)
: source_id_(source_id) {}
WgcCaptureSource::~WgcCaptureSource() = default;
bool WgcCaptureSource::IsCapturable() {
// If we can create a capture item, then we can capture it. Unfortunately,
// we can't cache this item because it may be created in a different COM
// apartment than where capture will eventually start from.
ComPtr<WGC::IGraphicsCaptureItem> item;
return SUCCEEDED(CreateCaptureItem(&item));
}
bool WgcCaptureSource::FocusOnSource() {
return false;
}
HRESULT WgcCaptureSource::GetCaptureItem(
ComPtr<WGC::IGraphicsCaptureItem>* result) {
HRESULT hr = S_OK;
if (!item_)
hr = CreateCaptureItem(&item_);
*result = item_;
return hr;
}
WgcCaptureSourceFactory::~WgcCaptureSourceFactory() = default;
WgcWindowSourceFactory::WgcWindowSourceFactory() = default;
WgcWindowSourceFactory::~WgcWindowSourceFactory() = default;
std::unique_ptr<WgcCaptureSource> WgcWindowSourceFactory::CreateCaptureSource(
DesktopCapturer::SourceId source_id) {
return std::make_unique<WgcWindowSource>(source_id);
}
WgcScreenSourceFactory::WgcScreenSourceFactory() = default;
WgcScreenSourceFactory::~WgcScreenSourceFactory() = default;
std::unique_ptr<WgcCaptureSource> WgcScreenSourceFactory::CreateCaptureSource(
DesktopCapturer::SourceId source_id) {
return std::make_unique<WgcScreenSource>(source_id);
}
WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id)
: WgcCaptureSource(source_id) {}
WgcWindowSource::~WgcWindowSource() = default;
DesktopVector WgcWindowSource::GetTopLeft() {
DesktopRect window_rect;
if (!GetWindowRect(reinterpret_cast<HWND>(GetSourceId()), &window_rect))
return DesktopVector();
return window_rect.top_left();
}
bool WgcWindowSource::IsCapturable() {
if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())))
return false;
return WgcCaptureSource::IsCapturable();
}
bool WgcWindowSource::FocusOnSource() {
if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())))
return false;
return ::BringWindowToTop(reinterpret_cast<HWND>(GetSourceId())) &&
::SetForegroundWindow(reinterpret_cast<HWND>(GetSourceId()));
}
HRESULT WgcWindowSource::CreateCaptureItem(
ComPtr<WGC::IGraphicsCaptureItem>* result) {
if (!ResolveCoreWinRTDelayload())
return E_FAIL;
ComPtr<IGraphicsCaptureItemInterop> interop;
HRESULT hr = GetActivationFactory<
IGraphicsCaptureItemInterop,
RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop);
if (FAILED(hr))
return hr;
ComPtr<WGC::IGraphicsCaptureItem> item;
hr = interop->CreateForWindow(reinterpret_cast<HWND>(GetSourceId()),
IID_PPV_ARGS(&item));
if (FAILED(hr))
return hr;
if (!item)
return E_HANDLE;
*result = std::move(item);
return hr;
}
WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id)
: WgcCaptureSource(source_id) {
// Getting the HMONITOR could fail if the source_id is invalid. In that case,
// we leave hmonitor_ uninitialized and |IsCapturable()| will fail.
HMONITOR hmon;
if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon))
hmonitor_ = hmon;
}
WgcScreenSource::~WgcScreenSource() = default;
DesktopVector WgcScreenSource::GetTopLeft() {
if (!hmonitor_)
return DesktopVector();
return GetMonitorRect(*hmonitor_).top_left();
}
bool WgcScreenSource::IsCapturable() {
if (!hmonitor_)
return false;
if (!IsMonitorValid(*hmonitor_))
return false;
return WgcCaptureSource::IsCapturable();
}
HRESULT WgcScreenSource::CreateCaptureItem(
ComPtr<WGC::IGraphicsCaptureItem>* result) {
if (!hmonitor_)
return E_ABORT;
if (!ResolveCoreWinRTDelayload())
return E_FAIL;
ComPtr<IGraphicsCaptureItemInterop> interop;
HRESULT hr = GetActivationFactory<
IGraphicsCaptureItemInterop,
RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop);
if (FAILED(hr))
return hr;
ComPtr<WGC::IGraphicsCaptureItem> item;
hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item));
if (FAILED(hr))
return hr;
if (!item)
return E_HANDLE;
*result = std::move(item);
return hr;
}
} // namespace webrtc

105
webrtc/wgc_capture_source.h Normal file
View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
#include <windows.graphics.capture.h>
#include <wrl/client.h>
#include <memory>
#include "absl/types/optional.h"
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/desktop_geometry.h"
namespace webrtc {
// Abstract class to represent the source that WGC-based capturers capture
// from. Could represent an application window or a screen. Consumers should use
// the appropriate Wgc*SourceFactory class to create WgcCaptureSource objects
// of the appropriate type.
class WgcCaptureSource {
public:
explicit WgcCaptureSource(DesktopCapturer::SourceId source_id);
virtual ~WgcCaptureSource();
virtual DesktopVector GetTopLeft() = 0;
virtual bool IsCapturable();
virtual bool FocusOnSource();
HRESULT GetCaptureItem(
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result);
DesktopCapturer::SourceId GetSourceId() { return source_id_; }
protected:
virtual HRESULT CreateCaptureItem(
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result) = 0;
private:
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
item_;
const DesktopCapturer::SourceId source_id_;
};
class WgcCaptureSourceFactory {
public:
virtual ~WgcCaptureSourceFactory();
virtual std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
DesktopCapturer::SourceId) = 0;
};
class WgcWindowSourceFactory final : public WgcCaptureSourceFactory {
public:
WgcWindowSourceFactory();
// Disallow copy and assign.
WgcWindowSourceFactory(const WgcWindowSourceFactory&) = delete;
WgcWindowSourceFactory& operator=(const WgcWindowSourceFactory&) = delete;
~WgcWindowSourceFactory() override;
std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
DesktopCapturer::SourceId) override;
};
class WgcScreenSourceFactory final : public WgcCaptureSourceFactory {
public:
WgcScreenSourceFactory();
WgcScreenSourceFactory(const WgcScreenSourceFactory&) = delete;
WgcScreenSourceFactory& operator=(const WgcScreenSourceFactory&) = delete;
~WgcScreenSourceFactory() override;
std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
DesktopCapturer::SourceId) override;
};
// Class for capturing application windows.
class WgcWindowSource final : public WgcCaptureSource {
public:
explicit WgcWindowSource(DesktopCapturer::SourceId source_id);
WgcWindowSource(const WgcWindowSource&) = delete;
WgcWindowSource& operator=(const WgcWindowSource&) = delete;
~WgcWindowSource() override;
DesktopVector GetTopLeft() override;
bool IsCapturable() override;
bool FocusOnSource() override;
private:
HRESULT CreateCaptureItem(
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result)
override;
};
// Class for capturing screens/monitors/displays.
class WgcScreenSource final : public WgcCaptureSource {
public:
explicit WgcScreenSource(DesktopCapturer::SourceId source_id);
WgcScreenSource(const WgcScreenSource&) = delete;
WgcScreenSource& operator=(const WgcScreenSource&) = delete;
~WgcScreenSource() override;
DesktopVector GetTopLeft() override;
bool IsCapturable() override;
private:
HRESULT CreateCaptureItem(
Microsoft::WRL::ComPtr<
ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result)
override;
// To maintain compatibility with other capturers, this class accepts a
// device index as it's SourceId. However, WGC requires we use an HMONITOR to
// describe which screen to capture. So, we internally convert the supplied
// device index into an HMONITOR when |IsCapturable()| is called.
absl::optional<HMONITOR> hmonitor_;
};
} // namespace webrtc
#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2021 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 "modules/desktop_capture/win/wgc_capture_source.h"
#include <windows.graphics.capture.h>
#include <wrl/client.h>
#include <utility>
#include "modules/desktop_capture/desktop_capture_types.h"
#include "modules/desktop_capture/desktop_geometry.h"
#include "modules/desktop_capture/win/screen_capture_utils.h"
#include "modules/desktop_capture/win/test_support/test_window.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/win/scoped_com_initializer.h"
#include "rtc_base/win/windows_version.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
const WCHAR kWindowTitle[] = L"WGC Capture Source Test Window";
const int kFirstXCoord = 25;
const int kFirstYCoord = 50;
const int kSecondXCoord = 50;
const int kSecondYCoord = 75;
enum SourceType { kWindowSource = 0, kScreenSource = 1 };
} // namespace
class WgcCaptureSourceTest : public ::testing::TestWithParam<SourceType> {
public:
void SetUp() override {
if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) {
RTC_LOG(LS_INFO)
<< "Skipping WgcCaptureSourceTests on Windows versions < RS5.";
GTEST_SKIP();
}
com_initializer_ =
std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
ASSERT_TRUE(com_initializer_->Succeeded());
}
void TearDown() override {
if (window_open_) {
DestroyTestWindow(window_info_);
}
}
void SetUpForWindowSource() {
window_info_ = CreateTestWindow(kWindowTitle);
window_open_ = true;
source_id_ = reinterpret_cast<DesktopCapturer::SourceId>(window_info_.hwnd);
source_factory_ = std::make_unique<WgcWindowSourceFactory>();
}
void SetUpForScreenSource() {
source_id_ = kFullDesktopScreenId;
source_factory_ = std::make_unique<WgcScreenSourceFactory>();
}
protected:
std::unique_ptr<ScopedCOMInitializer> com_initializer_;
std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
std::unique_ptr<WgcCaptureSource> source_;
DesktopCapturer::SourceId source_id_;
WindowInfo window_info_;
bool window_open_ = false;
};
// Window specific test
TEST_F(WgcCaptureSourceTest, WindowPosition) {
SetUpForWindowSource();
source_ = source_factory_->CreateCaptureSource(source_id_);
ASSERT_TRUE(source_);
EXPECT_EQ(source_->GetSourceId(), source_id_);
MoveTestWindow(window_info_.hwnd, kFirstXCoord, kFirstYCoord);
DesktopVector source_vector = source_->GetTopLeft();
EXPECT_EQ(source_vector.x(), kFirstXCoord);
EXPECT_EQ(source_vector.y(), kFirstYCoord);
MoveTestWindow(window_info_.hwnd, kSecondXCoord, kSecondYCoord);
source_vector = source_->GetTopLeft();
EXPECT_EQ(source_vector.x(), kSecondXCoord);
EXPECT_EQ(source_vector.y(), kSecondYCoord);
}
// Screen specific test
TEST_F(WgcCaptureSourceTest, ScreenPosition) {
SetUpForScreenSource();
source_ = source_factory_->CreateCaptureSource(source_id_);
ASSERT_TRUE(source_);
EXPECT_EQ(source_id_, source_->GetSourceId());
DesktopRect screen_rect = GetFullscreenRect();
DesktopVector source_vector = source_->GetTopLeft();
EXPECT_EQ(source_vector.x(), screen_rect.left());
EXPECT_EQ(source_vector.y(), screen_rect.top());
}
// Source agnostic test
TEST_P(WgcCaptureSourceTest, CreateSource) {
if (GetParam() == SourceType::kWindowSource) {
SetUpForWindowSource();
} else {
SetUpForScreenSource();
}
source_ = source_factory_->CreateCaptureSource(source_id_);
ASSERT_TRUE(source_);
EXPECT_EQ(source_id_, source_->GetSourceId());
EXPECT_TRUE(source_->IsCapturable());
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
item;
EXPECT_TRUE(SUCCEEDED(source_->GetCaptureItem(&item)));
EXPECT_TRUE(item);
}
INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
WgcCaptureSourceTest,
::testing::Values(SourceType::kWindowSource,
SourceType::kScreenSource));
} // namespace webrtc

218
webrtc/wgc_capturer_win.cc Normal file
View File

@@ -0,0 +1,218 @@
/*
* Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capturer_win.h"
#include <utility>
#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
#include "modules/desktop_capture/desktop_capture_types.h"
#include "modules/desktop_capture/win/wgc_desktop_frame.h"
#include "rtc_base/logging.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/metrics.h"
namespace WGC = ABI::Windows::Graphics::Capture;
using Microsoft::WRL::ComPtr;
namespace webrtc {
namespace {
enum class WgcCapturerResult {
kSuccess = 0,
kNoDirect3dDevice = 1,
kNoSourceSelected = 2,
kItemCreationFailure = 3,
kSessionStartFailure = 4,
kGetFrameFailure = 5,
kFrameDropped = 6,
kMaxValue = kFrameDropped
};
void RecordWgcCapturerResult(WgcCapturerResult error) {
RTC_HISTOGRAM_ENUMERATION("WebRTC.DesktopCapture.Win.WgcCapturerResult",
static_cast<int>(error),
static_cast<int>(WgcCapturerResult::kMaxValue));
}
} // namespace
WgcCapturerWin::WgcCapturerWin(
std::unique_ptr<WgcCaptureSourceFactory> source_factory,
std::unique_ptr<SourceEnumerator> source_enumerator)
: source_factory_(std::move(source_factory)),
source_enumerator_(std::move(source_enumerator)) {}
WgcCapturerWin::~WgcCapturerWin() = default;
// static
std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) {
return std::make_unique<WgcCapturerWin>(
std::make_unique<WgcWindowSourceFactory>(),
std::make_unique<WindowEnumerator>(
options.enumerate_current_process_windows()));
}
// static
std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawScreenCapturer(
const DesktopCaptureOptions& options) {
return std::make_unique<WgcCapturerWin>(
std::make_unique<WgcScreenSourceFactory>(),
std::make_unique<ScreenEnumerator>());
}
bool WgcCapturerWin::GetSourceList(SourceList* sources) {
return source_enumerator_->FindAllSources(sources);
}
bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) {
capture_source_ = source_factory_->CreateCaptureSource(id);
return capture_source_->IsCapturable();
}
bool WgcCapturerWin::FocusOnSelectedSource() {
if (!capture_source_)
return false;
return capture_source_->FocusOnSource();
}
void WgcCapturerWin::Start(Callback* callback) {
RTC_DCHECK(!callback_);
RTC_DCHECK(callback);
RecordCapturerImpl(DesktopCapturerId::kWgcCapturerWin);
callback_ = callback;
// Create a Direct3D11 device to share amongst the WgcCaptureSessions. Many
// parameters are nullptr as the implemention uses defaults that work well for
// us.
HRESULT hr = D3D11CreateDevice(
/*adapter=*/nullptr, D3D_DRIVER_TYPE_HARDWARE,
/*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
/*feature_levels=*/nullptr, /*feature_levels_size=*/0, D3D11_SDK_VERSION,
&d3d11_device_, /*feature_level=*/nullptr, /*device_context=*/nullptr);
if (hr == DXGI_ERROR_UNSUPPORTED) {
// If a hardware device could not be created, use WARP which is a high speed
// software device.
hr = D3D11CreateDevice(
/*adapter=*/nullptr, D3D_DRIVER_TYPE_WARP,
/*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
/*feature_levels=*/nullptr, /*feature_levels_size=*/0,
D3D11_SDK_VERSION, &d3d11_device_, /*feature_level=*/nullptr,
/*device_context=*/nullptr);
}
if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "Failed to create D3D11Device: " << hr;
}
}
void WgcCapturerWin::CaptureFrame() {
RTC_DCHECK(callback_);
if (!capture_source_) {
RTC_LOG(LS_ERROR) << "Source hasn't been selected";
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
/*frame=*/nullptr);
RecordWgcCapturerResult(WgcCapturerResult::kNoSourceSelected);
return;
}
if (!d3d11_device_) {
RTC_LOG(LS_ERROR) << "No D3D11D3evice, cannot capture.";
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
/*frame=*/nullptr);
RecordWgcCapturerResult(WgcCapturerResult::kNoDirect3dDevice);
return;
}
int64_t capture_start_time_nanos = rtc::TimeNanos();
HRESULT hr;
WgcCaptureSession* capture_session = nullptr;
std::map<SourceId, WgcCaptureSession>::iterator session_iter =
ongoing_captures_.find(capture_source_->GetSourceId());
if (session_iter == ongoing_captures_.end()) {
ComPtr<WGC::IGraphicsCaptureItem> item;
hr = capture_source_->GetCaptureItem(&item);
if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "Failed to create a GraphicsCaptureItem: " << hr;
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
/*frame=*/nullptr);
RecordWgcCapturerResult(WgcCapturerResult::kItemCreationFailure);
return;
}
std::pair<std::map<SourceId, WgcCaptureSession>::iterator, bool>
iter_success_pair = ongoing_captures_.emplace(
std::piecewise_construct,
std::forward_as_tuple(capture_source_->GetSourceId()),
std::forward_as_tuple(d3d11_device_, item));
RTC_DCHECK(iter_success_pair.second);
capture_session = &iter_success_pair.first->second;
} else {
capture_session = &session_iter->second;
}
if (!capture_session->IsCaptureStarted()) {
hr = capture_session->StartCapture();
if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "Failed to start capture: " << hr;
ongoing_captures_.erase(capture_source_->GetSourceId());
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
/*frame=*/nullptr);
RecordWgcCapturerResult(WgcCapturerResult::kSessionStartFailure);
return;
}
}
std::unique_ptr<DesktopFrame> frame;
hr = capture_session->GetFrame(&frame);
if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "GetFrame failed: " << hr;
ongoing_captures_.erase(capture_source_->GetSourceId());
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
/*frame=*/nullptr);
RecordWgcCapturerResult(WgcCapturerResult::kGetFrameFailure);
return;
}
if (!frame) {
callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY,
/*frame=*/nullptr);
RecordWgcCapturerResult(WgcCapturerResult::kFrameDropped);
return;
}
int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
rtc::kNumNanosecsPerMillisec;
RTC_HISTOGRAM_COUNTS_1000("WebRTC.DesktopCapture.Win.WgcCapturerFrameTime",
capture_time_ms);
frame->set_capture_time_ms(capture_time_ms);
frame->set_capturer_id(DesktopCapturerId::kWgcCapturerWin);
frame->set_may_contain_cursor(true);
frame->set_top_left(capture_source_->GetTopLeft());
RecordWgcCapturerResult(WgcCapturerResult::kSuccess);
callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS,
std::move(frame));
}
bool WgcCapturerWin::IsSourceBeingCaptured(DesktopCapturer::SourceId id) {
std::map<DesktopCapturer::SourceId, WgcCaptureSession>::iterator
session_iter = ongoing_captures_.find(id);
if (session_iter == ongoing_captures_.end())
return false;
return session_iter->second.IsCaptureStarted();
}
} // namespace webrtc

138
webrtc/wgc_capturer_win.h Normal file
View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
#include <d3d11.h>
#include <wrl/client.h>
#include <map>
#include <memory>
#include "modules/desktop_capture/desktop_capture_options.h"
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/win/screen_capture_utils.h"
#include "modules/desktop_capture/win/wgc_capture_session.h"
#include "modules/desktop_capture/win/wgc_capture_source.h"
#include "modules/desktop_capture/win/window_capture_utils.h"
namespace webrtc {
// WgcCapturerWin is initialized with an implementation of this base class,
// which it uses to find capturable sources of a particular type. This way,
// WgcCapturerWin can remain source-agnostic.
class SourceEnumerator {
public:
virtual ~SourceEnumerator() = default;
virtual bool FindAllSources(DesktopCapturer::SourceList* sources) = 0;
};
class WindowEnumerator final : public SourceEnumerator {
public:
explicit WindowEnumerator(bool enumerate_current_process_windows)
: enumerate_current_process_windows_(enumerate_current_process_windows) {}
WindowEnumerator(const WindowEnumerator&) = delete;
WindowEnumerator& operator=(const WindowEnumerator&) = delete;
~WindowEnumerator() override = default;
bool FindAllSources(DesktopCapturer::SourceList* sources) override {
// WGC fails to capture windows with the WS_EX_TOOLWINDOW style, so we
// provide it as a filter to ensure windows with the style are not returned.
return window_capture_helper_.EnumerateCapturableWindows(
sources, enumerate_current_process_windows_, WS_EX_TOOLWINDOW);
}
private:
WindowCaptureHelperWin window_capture_helper_;
bool enumerate_current_process_windows_;
};
class ScreenEnumerator final : public SourceEnumerator {
public:
ScreenEnumerator() = default;
ScreenEnumerator(const ScreenEnumerator&) = delete;
ScreenEnumerator& operator=(const ScreenEnumerator&) = delete;
~ScreenEnumerator() override = default;
bool FindAllSources(DesktopCapturer::SourceList* sources) override {
return webrtc::GetScreenList(sources);
}
};
// A capturer that uses the Window.Graphics.Capture APIs. It is suitable for
// both window and screen capture (but only one type per instance). Consumers
// should not instantiate this class directly, instead they should use
// |CreateRawWindowCapturer()| or |CreateRawScreenCapturer()| to receive a
// capturer appropriate for the type of source they want to capture.
class WgcCapturerWin : public DesktopCapturer {
public:
WgcCapturerWin(std::unique_ptr<WgcCaptureSourceFactory> source_factory,
std::unique_ptr<SourceEnumerator> source_enumerator);
WgcCapturerWin(const WgcCapturerWin&) = delete;
WgcCapturerWin& operator=(const WgcCapturerWin&) = delete;
~WgcCapturerWin() override;
static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
const DesktopCaptureOptions& options);
static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
const DesktopCaptureOptions& options);
// DesktopCapturer interface.
bool GetSourceList(SourceList* sources) override;
bool SelectSource(SourceId id) override;
bool FocusOnSelectedSource() override;
void Start(Callback* callback) override;
void CaptureFrame() override;
// Used in WgcCapturerTests.
bool IsSourceBeingCaptured(SourceId id);
private:
// Factory to create a WgcCaptureSource for us whenever SelectSource is
// called. Initialized at construction with a source-specific implementation.
std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
// The source enumerator helps us find capturable sources of the appropriate
// type. Initialized at construction with a source-specific implementation.
std::unique_ptr<SourceEnumerator> source_enumerator_;
// The WgcCaptureSource represents the source we are capturing. It tells us
// if the source is capturable and it creates the GraphicsCaptureItem for us.
std::unique_ptr<WgcCaptureSource> capture_source_;
// A map of all the sources we are capturing and the associated
// WgcCaptureSession. Frames for the current source (indicated via
// SelectSource) will be retrieved from the appropriate session when
// requested via CaptureFrame.
// This helps us efficiently capture multiple sources (e.g. when consumers
// are trying to display a list of available capture targets with thumbnails).
std::map<SourceId, WgcCaptureSession> ongoing_captures_;
// The callback that we deliver frames to, synchronously, before CaptureFrame
// returns.
Callback* callback_ = nullptr;
// A Direct3D11 device that is shared amongst the WgcCaptureSessions, who
// require one to perform the capture.
Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_;
};
} // namespace webrtc
#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_

View File

@@ -0,0 +1,421 @@
/*
* Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capturer_win.h"
#include <string>
#include <utility>
#include <vector>
#include "modules/desktop_capture/desktop_capture_options.h"
#include "modules/desktop_capture/desktop_capture_types.h"
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/win/test_support/test_window.h"
#include "modules/desktop_capture/win/window_capture_utils.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/thread.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/win/scoped_com_initializer.h"
#include "rtc_base/win/windows_version.h"
#include "system_wrappers/include/metrics.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
const char kWindowThreadName[] = "wgc_capturer_test_window_thread";
const WCHAR kWindowTitle[] = L"WGC Capturer Test Window";
const char kCapturerImplHistogram[] =
"WebRTC.DesktopCapture.Win.DesktopCapturerImpl";
const char kCapturerResultHistogram[] =
"WebRTC.DesktopCapture.Win.WgcCapturerResult";
const int kSuccess = 0;
const int kSessionStartFailure = 4;
const char kCaptureSessionResultHistogram[] =
"WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult";
const int kSourceClosed = 1;
const char kCaptureTimeHistogram[] =
"WebRTC.DesktopCapture.Win.WgcCapturerFrameTime";
const int kSmallWindowWidth = 200;
const int kSmallWindowHeight = 100;
const int kMediumWindowWidth = 300;
const int kMediumWindowHeight = 200;
const int kLargeWindowWidth = 400;
const int kLargeWindowHeight = 500;
// The size of the image we capture is slightly smaller than the actual size of
// the window.
const int kWindowWidthSubtrahend = 14;
const int kWindowHeightSubtrahend = 7;
// Custom message constants so we can direct our thread to close windows
// and quit running.
const UINT kNoOp = WM_APP;
const UINT kDestroyWindow = WM_APP + 1;
const UINT kQuitRunning = WM_APP + 2;
enum CaptureType { kWindowCapture = 0, kScreenCapture = 1 };
} // namespace
class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
public DesktopCapturer::Callback {
public:
void SetUp() override {
if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) {
RTC_LOG(LS_INFO)
<< "Skipping WgcCapturerWinTests on Windows versions < RS5.";
GTEST_SKIP();
}
com_initializer_ =
std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
EXPECT_TRUE(com_initializer_->Succeeded());
}
void SetUpForWindowCapture(int window_width = kMediumWindowWidth,
int window_height = kMediumWindowHeight) {
capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
DesktopCaptureOptions::CreateDefault());
CreateWindowOnSeparateThread(window_width, window_height);
StartWindowThreadMessageLoop();
source_id_ = GetTestWindowIdFromSourceList();
}
void SetUpForScreenCapture() {
capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
DesktopCaptureOptions::CreateDefault());
source_id_ = GetScreenIdFromSourceList();
}
void TearDown() override {
if (window_open_) {
CloseTestWindow();
}
}
// The window must live on a separate thread so that we can run a message pump
// without blocking the test thread. This is necessary if we are interested in
// having GraphicsCaptureItem events (i.e. the Closed event) fire, and it more
// closely resembles how capture works in the wild.
void CreateWindowOnSeparateThread(int window_width, int window_height) {
window_thread_ = rtc::Thread::Create();
window_thread_->SetName(kWindowThreadName, nullptr);
window_thread_->Start();
window_thread_->Invoke<void>(RTC_FROM_HERE, [this, window_width,
window_height]() {
window_thread_id_ = GetCurrentThreadId();
window_info_ =
CreateTestWindow(kWindowTitle, window_height, window_width);
window_open_ = true;
while (!IsWindowResponding(window_info_.hwnd)) {
RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in "
"WgcWindowCaptureTest.";
}
while (!IsWindowValidAndVisible(window_info_.hwnd)) {
RTC_LOG(LS_INFO) << "Waiting for test window to be visible in "
"WgcWindowCaptureTest.";
}
});
ASSERT_TRUE(window_thread_->RunningForTest());
ASSERT_FALSE(window_thread_->IsCurrent());
}
void StartWindowThreadMessageLoop() {
window_thread_->PostTask(RTC_FROM_HERE, [this]() {
MSG msg;
BOOL gm;
while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
::DispatchMessage(&msg);
if (msg.message == kDestroyWindow) {
DestroyTestWindow(window_info_);
}
if (msg.message == kQuitRunning) {
PostQuitMessage(0);
}
}
});
}
void CloseTestWindow() {
::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0);
::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0);
window_thread_->Stop();
window_open_ = false;
}
DesktopCapturer::SourceId GetTestWindowIdFromSourceList() {
// Frequently, the test window will not show up in GetSourceList because it
// was created too recently. Since we are confident the window will be found
// eventually we loop here until we find it.
intptr_t src_id;
do {
DesktopCapturer::SourceList sources;
EXPECT_TRUE(capturer_->GetSourceList(&sources));
auto it = std::find_if(
sources.begin(), sources.end(),
[&](const DesktopCapturer::Source& src) {
return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
});
src_id = it->id;
} while (src_id != reinterpret_cast<intptr_t>(window_info_.hwnd));
return src_id;
}
DesktopCapturer::SourceId GetScreenIdFromSourceList() {
DesktopCapturer::SourceList sources;
EXPECT_TRUE(capturer_->GetSourceList(&sources));
EXPECT_GT(sources.size(), 0ULL);
return sources[0].id;
}
void DoCapture() {
// Sometimes the first few frames are empty becaues the capture engine is
// still starting up. We also may drop a few frames when the window is
// resized or un-minimized.
do {
capturer_->CaptureFrame();
} while (result_ == DesktopCapturer::Result::ERROR_TEMPORARY);
EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
EXPECT_TRUE(frame_);
EXPECT_GT(metrics::NumEvents(kCapturerResultHistogram, kSuccess),
successful_captures_);
++successful_captures_;
}
void ValidateFrame(int expected_width, int expected_height) {
EXPECT_EQ(frame_->size().width(), expected_width - kWindowWidthSubtrahend);
EXPECT_EQ(frame_->size().height(),
expected_height - kWindowHeightSubtrahend);
// Verify the buffer contains as much data as it should, and that the right
// colors are found.
int data_length = frame_->stride() * frame_->size().height();
// The first and last pixel should have the same color because they will be
// from the border of the window.
// Pixels have 4 bytes of data so the whole pixel needs a uint32_t to fit.
uint32_t first_pixel = static_cast<uint32_t>(*frame_->data());
uint32_t last_pixel = static_cast<uint32_t>(
*(frame_->data() + data_length - DesktopFrame::kBytesPerPixel));
EXPECT_EQ(first_pixel, last_pixel);
// Let's also check a pixel from the middle of the content area, which the
// TestWindow will paint a consistent color for us to verify.
uint8_t* middle_pixel = frame_->data() + (data_length / 2);
int sub_pixel_offset = DesktopFrame::kBytesPerPixel / 4;
EXPECT_EQ(*middle_pixel, kTestWindowBValue);
middle_pixel += sub_pixel_offset;
EXPECT_EQ(*middle_pixel, kTestWindowGValue);
middle_pixel += sub_pixel_offset;
EXPECT_EQ(*middle_pixel, kTestWindowRValue);
middle_pixel += sub_pixel_offset;
// The window is opaque so we expect 0xFF for the Alpha channel.
EXPECT_EQ(*middle_pixel, 0xFF);
}
// DesktopCapturer::Callback interface
// The capturer synchronously invokes this method before |CaptureFrame()|
// returns.
void OnCaptureResult(DesktopCapturer::Result result,
std::unique_ptr<DesktopFrame> frame) override {
result_ = result;
frame_ = std::move(frame);
}
protected:
std::unique_ptr<ScopedCOMInitializer> com_initializer_;
DWORD window_thread_id_;
std::unique_ptr<rtc::Thread> window_thread_;
WindowInfo window_info_;
intptr_t source_id_;
bool window_open_ = false;
DesktopCapturer::Result result_;
int successful_captures_ = 0;
std::unique_ptr<DesktopFrame> frame_;
std::unique_ptr<DesktopCapturer> capturer_;
};
TEST_P(WgcCapturerWinTest, SelectValidSource) {
if (GetParam() == CaptureType::kWindowCapture) {
SetUpForWindowCapture();
} else {
SetUpForScreenCapture();
}
EXPECT_TRUE(capturer_->SelectSource(source_id_));
}
TEST_P(WgcCapturerWinTest, SelectInvalidSource) {
if (GetParam() == CaptureType::kWindowCapture) {
capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
DesktopCaptureOptions::CreateDefault());
source_id_ = kNullWindowId;
} else {
capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
DesktopCaptureOptions::CreateDefault());
source_id_ = kInvalidScreenId;
}
EXPECT_FALSE(capturer_->SelectSource(source_id_));
}
TEST_P(WgcCapturerWinTest, Capture) {
if (GetParam() == CaptureType::kWindowCapture) {
SetUpForWindowCapture();
} else {
SetUpForScreenCapture();
}
EXPECT_TRUE(capturer_->SelectSource(source_id_));
capturer_->Start(this);
EXPECT_GE(metrics::NumEvents(kCapturerImplHistogram,
DesktopCapturerId::kWgcCapturerWin),
1);
DoCapture();
EXPECT_GT(frame_->size().width(), 0);
EXPECT_GT(frame_->size().height(), 0);
}
TEST_P(WgcCapturerWinTest, CaptureTime) {
if (GetParam() == CaptureType::kWindowCapture) {
SetUpForWindowCapture();
} else {
SetUpForScreenCapture();
}
EXPECT_TRUE(capturer_->SelectSource(source_id_));
capturer_->Start(this);
int64_t start_time;
do {
start_time = rtc::TimeNanos();
capturer_->CaptureFrame();
} while (result_ == DesktopCapturer::Result::ERROR_TEMPORARY);
int capture_time_ms =
(rtc::TimeNanos() - start_time) / rtc::kNumNanosecsPerMillisec;
EXPECT_TRUE(frame_);
// The test may measure the time slightly differently than the capturer. So we
// just check if it's within 5 ms.
EXPECT_NEAR(frame_->capture_time_ms(), capture_time_ms, 5);
EXPECT_GE(
metrics::NumEvents(kCaptureTimeHistogram, frame_->capture_time_ms()), 1);
}
INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
WgcCapturerWinTest,
::testing::Values(CaptureType::kWindowCapture,
CaptureType::kScreenCapture));
// Monitor specific tests.
TEST_F(WgcCapturerWinTest, FocusOnMonitor) {
SetUpForScreenCapture();
EXPECT_TRUE(capturer_->SelectSource(0));
// You can't set focus on a monitor.
EXPECT_FALSE(capturer_->FocusOnSelectedSource());
}
TEST_F(WgcCapturerWinTest, CaptureAllMonitors) {
SetUpForScreenCapture();
EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
capturer_->Start(this);
DoCapture();
EXPECT_GT(frame_->size().width(), 0);
EXPECT_GT(frame_->size().height(), 0);
}
// Window specific tests.
TEST_F(WgcCapturerWinTest, FocusOnWindow) {
capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
DesktopCaptureOptions::CreateDefault());
window_info_ = CreateTestWindow(kWindowTitle);
source_id_ = GetScreenIdFromSourceList();
EXPECT_TRUE(capturer_->SelectSource(source_id_));
EXPECT_TRUE(capturer_->FocusOnSelectedSource());
HWND hwnd = reinterpret_cast<HWND>(source_id_);
EXPECT_EQ(hwnd, ::GetActiveWindow());
EXPECT_EQ(hwnd, ::GetForegroundWindow());
EXPECT_EQ(hwnd, ::GetFocus());
DestroyTestWindow(window_info_);
}
TEST_F(WgcCapturerWinTest, SelectMinimizedWindow) {
SetUpForWindowCapture();
MinimizeTestWindow(reinterpret_cast<HWND>(source_id_));
EXPECT_FALSE(capturer_->SelectSource(source_id_));
UnminimizeTestWindow(reinterpret_cast<HWND>(source_id_));
EXPECT_TRUE(capturer_->SelectSource(source_id_));
}
TEST_F(WgcCapturerWinTest, SelectClosedWindow) {
SetUpForWindowCapture();
EXPECT_TRUE(capturer_->SelectSource(source_id_));
CloseTestWindow();
EXPECT_FALSE(capturer_->SelectSource(source_id_));
}
TEST_F(WgcCapturerWinTest, UnsupportedWindowStyle) {
// Create a window with the WS_EX_TOOLWINDOW style, which WGC does not
// support.
window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth,
kMediumWindowHeight, WS_EX_TOOLWINDOW);
capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
DesktopCaptureOptions::CreateDefault());
DesktopCapturer::SourceList sources;
EXPECT_TRUE(capturer_->GetSourceList(&sources));
auto it = std::find_if(
sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) {
return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
});
// We should not find the window, since we filter for unsupported styles.
EXPECT_EQ(it, sources.end());
DestroyTestWindow(window_info_);
}
TEST_F(WgcCapturerWinTest, IncreaseWindowSizeMidCapture) {
SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight);
EXPECT_TRUE(capturer_->SelectSource(source_id_));
capturer_->Start(this);
DoCapture();
ValidateFrame(kSmallWindowWidth, kSmallWindowHeight);
ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
DoCapture();
// We don't expect to see the new size until the next capture, as the frame
// pool hadn't had a chance to resize yet to fit the new, larger image.
DoCapture();
ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
DoCapture();
DoCapture();
ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
}
TEST_F(WgcCapturerWinTest, ReduceWindowSizeMidCapture) {
SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight);
EXPECT_TRUE(capturer_->SelectSource(source_id_));
capturer_->Start(this);
DoCapture();
ValidateFrame(kLargeWindowWidth, kLargeWindowHeight);
ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
// We expect to see the new size immediately because the image data has shrunk
// and will fit in the existing buffer.
DoCapture();
ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
DoCapture();
ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
}
TEST_F(WgcCapturerWinTest, MinimizeWindowMidCapture) {
SetUpForWindowCapture();
EXPECT_TRUE(capturer_->SelectSource(source_id_));
capturer_->Start(this);
// Minmize the window and capture should continue but return temporary errors.
MinimizeTestWindow(window_info_.hwnd);
for (int i = 0; i < 10; ++i) {
capturer_->CaptureFrame();
EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_TEMPORARY);
}
// Reopen the window and the capture should continue normally.
UnminimizeTestWindow(window_info_.hwnd);
DoCapture();
// We can't verify the window size here because the test window does not
// repaint itself after it is unminimized, but capturing successfully is still
// a good test.
}
TEST_F(WgcCapturerWinTest, CloseWindowMidCapture) {
SetUpForWindowCapture();
EXPECT_TRUE(capturer_->SelectSource(source_id_));
capturer_->Start(this);
DoCapture();
ValidateFrame(kMediumWindowWidth, kMediumWindowHeight);
CloseTestWindow();
// We need to call GetMessage to trigger the Closed event and the capturer's
// event handler for it. If we are too early and the Closed event hasn't
// arrived yet we should keep trying until the capturer receives it and stops.
auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get());
while (wgc_capturer->IsSourceBeingCaptured(source_id_)) {
// Since the capturer handles the Closed message, there will be no message
// for us and GetMessage will hang, unless we send ourselves a message
// first.
::PostThreadMessage(GetCurrentThreadId(), kNoOp, 0, 0);
MSG msg;
::GetMessage(&msg, NULL, 0, 0);
::DispatchMessage(&msg);
}
// Occasionally, one last frame will have made it into the frame pool before
// the window closed. The first call will consume it, and in that case we need
// to make one more call to CaptureFrame.
capturer_->CaptureFrame();
if (result_ == DesktopCapturer::Result::SUCCESS)
capturer_->CaptureFrame();
EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure),
1);
EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed),
1);
EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT);
}
} // namespace webrtc

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_desktop_frame.h"
#include <utility>
namespace webrtc {
WgcDesktopFrame::WgcDesktopFrame(DesktopSize size,
int stride,
std::vector<uint8_t>&& image_data)
: DesktopFrame(size, stride, image_data.data(), nullptr),
image_data_(std::move(image_data)) {}
WgcDesktopFrame::~WgcDesktopFrame() = default;
} // namespace webrtc

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
#define MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
#include <d3d11.h>
#include <wrl/client.h>
#include <memory>
#include <vector>
#include "desktop_frame.h"
#include "desktop_geometry.h"
namespace webrtc {
// DesktopFrame implementation used by capturers that use the
// Windows.Graphics.Capture API.
class WgcDesktopFrame final : public DesktopFrame {
public:
// WgcDesktopFrame receives an rvalue reference to the |image_data| vector
// so that it can take ownership of it (and avoid a copy).
WgcDesktopFrame(DesktopSize size,
int stride,
std::vector<uint8_t>&& image_data);
WgcDesktopFrame(const WgcDesktopFrame&) = delete;
WgcDesktopFrame& operator=(const WgcDesktopFrame&) = delete;
~WgcDesktopFrame() override;
private:
std::vector<uint8_t> image_data_;
};
} // namespace webrtc
#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_

View File

@@ -35,6 +35,6 @@ target("remote_desk")
add_packages("log")
add_packages("ffmpeg")
add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil")
add_files("*.cpp")
add_files("dll/*.cpp")
add_includedirs("../../src/interface")
-- add_links("SDL2main", "SDL2-static")