Use kcp as QoS module

This commit is contained in:
dijunkun
2023-08-30 17:44:22 +08:00
parent 1e90d99980
commit 72d4982f54
34 changed files with 157 additions and 3119 deletions

View File

@@ -1,85 +0,0 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED <20>AS IS? WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "pch.h"
#include "App.h"
#include "SimpleCapture.h"
using namespace winrt;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::Graphics::Capture;
void App::Initialize(ContainerVisual const &root) {
auto queue = DispatcherQueue::GetForCurrentThread();
m_compositor = root.Compositor();
m_root = m_compositor.CreateContainerVisual();
m_content = m_compositor.CreateSpriteVisual();
m_brush = m_compositor.CreateSurfaceBrush();
m_root.RelativeSizeAdjustment({1, 1});
root.Children().InsertAtTop(m_root);
m_content.AnchorPoint({0.5f, 0.5f});
m_content.RelativeOffsetAdjustment({0.5f, 0.5f, 0});
m_content.RelativeSizeAdjustment({1, 1});
m_content.Size({-80, -80});
m_content.Brush(m_brush);
m_brush.HorizontalAlignmentRatio(0.5f);
m_brush.VerticalAlignmentRatio(0.5f);
m_brush.Stretch(CompositionStretch::Uniform);
auto shadow = m_compositor.CreateDropShadow();
shadow.Mask(m_brush);
m_content.Shadow(shadow);
m_root.Children().InsertAtTop(m_content);
auto d3dDevice = CreateD3DDevice();
auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
m_device = CreateDirect3DDevice(dxgiDevice.get());
}
void App::StartCapture(HWND hwnd) {
if (m_capture) {
m_capture->Close();
m_capture = nullptr;
}
auto item = CreateCaptureItemForWindow(hwnd);
m_capture = std::make_unique<SimpleCapture>(m_device, item);
auto surface = m_capture->CreateSurface(m_compositor);
m_brush.Surface(surface);
m_capture->StartCapture();
}
void App::StartCapture(HMONITOR hmonitor) {
if (m_capture) {
m_capture->Close();
m_capture = nullptr;
}
auto item = CreateCaptureItemForMonitor(hmonitor);
m_capture = std::make_unique<SimpleCapture>(m_device, item);
auto surface = m_capture->CreateSurface(m_compositor);
m_brush.Surface(surface);
m_capture->StartCapture();
}

View File

@@ -1,24 +0,0 @@
#pragma once
class SimpleCapture;
class App {
public:
App() {}
~App() {}
void Initialize(winrt::Windows::UI::Composition::ContainerVisual const &root);
void StartCapture(HWND hwnd);
void StartCapture(HMONITOR hmonitor);
private:
winrt::Windows::UI::Composition::Compositor m_compositor{nullptr};
winrt::Windows::UI::Composition::ContainerVisual m_root{nullptr};
winrt::Windows::UI::Composition::SpriteVisual m_content{nullptr};
winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr};
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{
nullptr};
std::unique_ptr<SimpleCapture> m_capture{nullptr};
};

View File

@@ -1,217 +0,0 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED <20>AS IS? WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "SimpleCapture.h"
#include "pch.h"
using namespace winrt;
using namespace Windows;
using namespace Windows::Foundation;
using namespace Windows::System;
using namespace Windows::Graphics;
using namespace Windows::Graphics::Capture;
using namespace Windows::Graphics::DirectX;
using namespace Windows::Graphics::DirectX::Direct3D11;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
SimpleCapture::SimpleCapture(IDirect3DDevice const &device,
GraphicsCaptureItem const &item) {
m_item = item;
m_device = device;
// Set up
auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
d3dDevice->GetImmediateContext(m_d3dContext.put());
auto size = m_item.Size();
m_swapChain = CreateDXGISwapChain(
d3dDevice, static_cast<uint32_t>(size.Width),
static_cast<uint32_t>(size.Height),
static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized), 2);
// Create framepool, define pixel format (DXGI_FORMAT_B8G8R8A8_UNORM), and
// frame size.
m_framePool = Direct3D11CaptureFramePool::Create(
m_device, DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, size);
m_session = m_framePool.CreateCaptureSession(m_item);
m_lastSize = size;
m_frameArrived = m_framePool.FrameArrived(
auto_revoke, {this, &SimpleCapture::OnFrameArrived});
}
// Start sending capture frames
void SimpleCapture::StartCapture() {
CheckClosed();
m_session.StartCapture();
}
ICompositionSurface SimpleCapture::CreateSurface(Compositor const &compositor) {
CheckClosed();
return CreateCompositionSurfaceForSwapChain(compositor, m_swapChain.get());
}
// Process captured frames
void SimpleCapture::Close() {
auto expected = false;
if (m_closed.compare_exchange_strong(expected, true)) {
m_frameArrived.revoke();
m_framePool.Close();
m_session.Close();
m_swapChain = nullptr;
m_framePool = nullptr;
m_session = nullptr;
m_item = nullptr;
}
}
void SimpleCapture::OnFrameArrived(
Direct3D11CaptureFramePool const &sender,
winrt::Windows::Foundation::IInspectable const &) {
auto newSize = false;
{
auto frame = sender.TryGetNextFrame();
auto frameContentSize = frame.ContentSize();
if (frameContentSize.Width != m_lastSize.Width ||
frameContentSize.Height != m_lastSize.Height) {
// The thing we have been capturing has changed size.
// We need to resize our swap chain first, then blit the pixels.
// After we do that, retire the frame and then recreate our frame pool.
newSize = true;
m_lastSize = frameContentSize;
m_swapChain->ResizeBuffers(
2, static_cast<uint32_t>(m_lastSize.Width),
static_cast<uint32_t>(m_lastSize.Height),
static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized),
0);
}
// copy to swapChain
{
auto frameSurface =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
com_ptr<ID3D11Texture2D> backBuffer;
check_hresult(m_swapChain->GetBuffer(0, guid_of<ID3D11Texture2D>(),
backBuffer.put_void()));
m_d3dContext->CopyResource(backBuffer.get(), frameSurface.get());
DXGI_PRESENT_PARAMETERS presentParameters = {0};
m_swapChain->Present1(1, 0, &presentParameters);
}
// copy to mapped texture
{
auto frameSurface =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
if (!m_mappedTexture || newSize) CreateMappedTexture(frameSurface);
m_d3dContext->CopyResource(m_mappedTexture.get(), frameSurface.get());
D3D11_MAPPED_SUBRESOURCE mapInfo;
m_d3dContext->Map(m_mappedTexture.get(), 0, D3D11_MAP_READ,
D3D11_MAP_FLAG_DO_NOT_WAIT, &mapInfo);
// copy data from mapInfo.pData
#if 1
if (mapInfo.pData) {
static unsigned char *buffer = nullptr;
if (buffer && newSize) delete[] buffer;
if (!buffer)
buffer = new unsigned char[frameContentSize.Width *
frameContentSize.Height * 4];
int dstRowPitch = frameContentSize.Width * 4;
for (int h = 0; h < frameContentSize.Height; h++) {
memcpy_s(buffer + h * dstRowPitch, dstRowPitch,
(BYTE *)mapInfo.pData + h * mapInfo.RowPitch,
min(mapInfo.RowPitch, dstRowPitch));
}
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = frameContentSize.Width;
bi.biHeight = frameContentSize.Height * (-1);
bi.biPlanes = 1;
bi.biBitCount = 32; // should get from system color bits
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
BITMAPFILEHEADER bf;
bf.bfType = 0x4d42;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bf.bfSize =
bf.bfOffBits + frameContentSize.Width * frameContentSize.Height * 4;
FILE *fp = nullptr;
fopen_s(&fp, ".\\save.bmp", "wb+");
fwrite(&bf, 1, sizeof(bf), fp);
fwrite(&bi, 1, sizeof(bi), fp);
fwrite(buffer, 1, frameContentSize.Width * frameContentSize.Height * 4,
fp);
fflush(fp);
fclose(fp);
}
#endif
m_d3dContext->Unmap(m_mappedTexture.get(), 0);
}
}
if (newSize) {
m_framePool.Recreate(m_device, DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, m_lastSize);
}
}
HRESULT
SimpleCapture::CreateMappedTexture(winrt::com_ptr<ID3D11Texture2D> src_texture,
UINT width, UINT height) {
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;
auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
return d3dDevice->CreateTexture2D(&map_desc, nullptr, m_mappedTexture.put());
}

View File

@@ -1,49 +0,0 @@
#pragma once
class SimpleCapture {
public:
SimpleCapture(
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const
&device,
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &item);
~SimpleCapture() { Close(); }
void StartCapture();
winrt::Windows::UI::Composition::ICompositionSurface
CreateSurface(winrt::Windows::UI::Composition::Compositor const &compositor);
void Close();
private:
void OnFrameArrived(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const
&sender,
winrt::Windows::Foundation::IInspectable const &args);
void CheckClosed() {
if (m_closed.load() == true) {
throw winrt::hresult_error(RO_E_CLOSED);
}
}
HRESULT
CreateMappedTexture(winrt::com_ptr<ID3D11Texture2D> src_texture,
UINT width = 0, UINT height = 0);
private:
winrt::Windows::Graphics::Capture::GraphicsCaptureItem m_item{nullptr};
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool m_framePool{
nullptr};
winrt::Windows::Graphics::Capture::GraphicsCaptureSession m_session{nullptr};
winrt::Windows::Graphics::SizeInt32 m_lastSize;
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{
nullptr};
winrt::com_ptr<IDXGISwapChain1> m_swapChain{nullptr};
winrt::com_ptr<ID3D11DeviceContext> m_d3dContext{nullptr};
winrt::com_ptr<ID3D11Texture2D> m_mappedTexture{nullptr};
std::atomic<bool> m_closed = false;
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
FrameArrived_revoker m_frameArrived;
};

View File

@@ -1,50 +0,0 @@
#pragma once
#include <dwmapi.h>
struct Monitor {
public:
Monitor(nullptr_t) {}
Monitor(HMONITOR hmonitor, std::wstring &className, bool isPrimary) {
m_hmonitor = hmonitor;
m_className = className;
m_bIsPrimary = isPrimary;
}
HMONITOR Hmonitor() const noexcept { return m_hmonitor; }
std::wstring ClassName() const noexcept { return m_className; }
bool IsPrimary() const noexcept { return m_bIsPrimary; }
private:
HMONITOR m_hmonitor;
std::wstring m_className;
bool m_bIsPrimary;
};
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;
auto monitors = ((std::vector<Monitor> *)data);
std::wstring name = info_ex.szDevice;
auto monitor =
Monitor(hmonitor, name, info_ex.dwFlags & MONITORINFOF_PRIMARY);
monitors->emplace_back(monitor);
return true;
}
std::vector<Monitor> EnumerateMonitors() {
std::vector<Monitor> monitors;
::EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&monitors);
return monitors;
}

View File

@@ -1,101 +0,0 @@
#pragma once
#include <dwmapi.h>
struct Window {
public:
Window(nullptr_t) {}
Window(HWND hwnd, std::wstring const &title, std::wstring &className) {
m_hwnd = hwnd;
m_title = title;
m_className = className;
}
HWND Hwnd() const noexcept { return m_hwnd; }
std::wstring Title() const noexcept { return m_title; }
std::wstring ClassName() const noexcept { return m_className; }
private:
HWND m_hwnd;
std::wstring m_title;
std::wstring m_className;
};
std::wstring GetClassName(HWND hwnd) {
std::array<WCHAR, 1024> className;
::GetClassName(hwnd, className.data(), (int)className.size());
std::wstring title(className.data());
return title;
}
std::wstring GetWindowText(HWND hwnd) {
std::array<WCHAR, 1024> windowText;
::GetWindowText(hwnd, windowText.data(), (int)windowText.size());
std::wstring title(windowText.data());
return title;
}
bool IsAltTabWindow(Window const &window) {
HWND hwnd = window.Hwnd();
HWND shellWindow = GetShellWindow();
auto title = window.Title();
auto className = window.ClassName();
if (hwnd == shellWindow) {
return false;
}
if (title.length() == 0) {
return false;
}
if (!IsWindowVisible(hwnd)) {
return false;
}
if (GetAncestor(hwnd, GA_ROOT) != hwnd) {
return false;
}
LONG style = GetWindowLong(hwnd, GWL_STYLE);
if (!((style & WS_DISABLED) != WS_DISABLED)) {
return false;
}
DWORD cloaked = FALSE;
HRESULT hrTemp =
DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
if (SUCCEEDED(hrTemp) && cloaked == DWM_CLOAKED_SHELL) {
return false;
}
return true;
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
auto class_name = GetClassName(hwnd);
auto title = GetWindowText(hwnd);
auto window = Window(hwnd, title, class_name);
if (!IsAltTabWindow(window)) {
return TRUE;
}
std::vector<Window> &windows =
*reinterpret_cast<std::vector<Window> *>(lParam);
windows.push_back(window);
return TRUE;
}
const std::vector<Window> EnumerateWindows() {
std::vector<Window> windows;
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&windows));
return windows;
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include <windows.graphics.capture.h>
#include <windows.graphics.capture.interop.h>
#include <winrt/Windows.Graphics.Capture.h>
inline auto CreateCaptureItemForWindow(HWND hwnd) {
auto activation_factory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForWindow(
hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void **>(winrt::put_abi(item)));
return item;
}
inline auto CreateCaptureItemForMonitor(HMONITOR hmonitor) {
auto activation_factory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForMonitor(
hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void **>(winrt::put_abi(item)));
return item;
}

View File

@@ -1,69 +0,0 @@
#pragma once
#include <d2d1_1.h>
#include <windows.ui.composition.interop.h>
#include <winrt/Windows.UI.Composition.h>
inline auto CreateCompositionGraphicsDevice(
winrt::Windows::UI::Composition::Compositor const &compositor,
::IUnknown *device) {
winrt::Windows::UI::Composition::CompositionGraphicsDevice graphicsDevice{
nullptr};
auto compositorInterop =
compositor.as<ABI::Windows::UI::Composition::ICompositorInterop>();
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositionGraphicsDevice>
graphicsInterop;
winrt::check_hresult(
compositorInterop->CreateGraphicsDevice(device, graphicsInterop.put()));
winrt::check_hresult(graphicsInterop->QueryInterface(
winrt::guid_of<
winrt::Windows::UI::Composition::CompositionGraphicsDevice>(),
reinterpret_cast<void **>(winrt::put_abi(graphicsDevice))));
return graphicsDevice;
}
inline void ResizeSurface(
winrt::Windows::UI::Composition::CompositionDrawingSurface const &surface,
winrt::Windows::Foundation::Size const &size) {
auto surfaceInterop = surface.as<
ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>();
SIZE newSize = {};
newSize.cx = static_cast<LONG>(std::round(size.Width));
newSize.cy = static_cast<LONG>(std::round(size.Height));
winrt::check_hresult(surfaceInterop->Resize(newSize));
}
inline auto SurfaceBeginDraw(
winrt::Windows::UI::Composition::CompositionDrawingSurface const &surface) {
auto surfaceInterop = surface.as<
ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>();
winrt::com_ptr<ID2D1DeviceContext> context;
POINT offset = {};
winrt::check_hresult(surfaceInterop->BeginDraw(
nullptr, __uuidof(ID2D1DeviceContext), context.put_void(), &offset));
context->SetTransform(
D2D1::Matrix3x2F::Translation((FLOAT)offset.x, (FLOAT)offset.y));
return context;
}
inline void SurfaceEndDraw(
winrt::Windows::UI::Composition::CompositionDrawingSurface const &surface) {
auto surfaceInterop = surface.as<
ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>();
winrt::check_hresult(surfaceInterop->EndDraw());
}
inline auto CreateCompositionSurfaceForSwapChain(
winrt::Windows::UI::Composition::Compositor const &compositor,
::IUnknown *swapChain) {
winrt::Windows::UI::Composition::ICompositionSurface surface{nullptr};
auto compositorInterop =
compositor.as<ABI::Windows::UI::Composition::ICompositorInterop>();
winrt::com_ptr<ABI::Windows::UI::Composition::ICompositionSurface>
surfaceInterop;
winrt::check_hresult(compositorInterop->CreateCompositionSurfaceForSwapChain(
swapChain, surfaceInterop.put()));
winrt::check_hresult(surfaceInterop->QueryInterface(
winrt::guid_of<winrt::Windows::UI::Composition::ICompositionSurface>(),
reinterpret_cast<void **>(winrt::put_abi(surface))));
return surface;
}

View File

@@ -1,132 +0,0 @@
#pragma once
#include "composition.interop.h"
struct SurfaceContext {
public:
SurfaceContext(std::nullptr_t) {}
SurfaceContext(
winrt::Windows::UI::Composition::CompositionDrawingSurface surface) {
m_surface = surface;
m_d2dContext = SurfaceBeginDraw(m_surface);
}
~SurfaceContext() {
SurfaceEndDraw(m_surface);
m_d2dContext = nullptr;
m_surface = nullptr;
}
winrt::com_ptr<ID2D1DeviceContext> GetDeviceContext() { return m_d2dContext; }
private:
winrt::com_ptr<ID2D1DeviceContext> m_d2dContext;
winrt::Windows::UI::Composition::CompositionDrawingSurface m_surface{nullptr};
};
struct D3D11DeviceLock {
public:
D3D11DeviceLock(std::nullopt_t) {}
D3D11DeviceLock(ID3D11Multithread *pMultithread) {
m_multithread.copy_from(pMultithread);
m_multithread->Enter();
}
~D3D11DeviceLock() {
m_multithread->Leave();
m_multithread = nullptr;
}
private:
winrt::com_ptr<ID3D11Multithread> m_multithread;
};
inline auto CreateWICFactory() {
winrt::com_ptr<IWICImagingFactory2> wicFactory;
winrt::check_hresult(::CoCreateInstance(
CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
winrt::guid_of<IWICImagingFactory>(), wicFactory.put_void()));
return wicFactory;
}
inline auto CreateD2DDevice(winrt::com_ptr<ID2D1Factory1> const &factory,
winrt::com_ptr<ID3D11Device> const &device) {
winrt::com_ptr<ID2D1Device> result;
winrt::check_hresult(
factory->CreateDevice(device.as<IDXGIDevice>().get(), result.put()));
return result;
}
inline auto CreateD3DDevice(D3D_DRIVER_TYPE const type,
winrt::com_ptr<ID3D11Device> &device) {
WINRT_ASSERT(!device);
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
//#ifdef _DEBUG
// flags |= D3D11_CREATE_DEVICE_DEBUG;
//#endif
return D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
D3D11_SDK_VERSION, device.put(), nullptr, nullptr);
}
inline auto CreateD3DDevice() {
winrt::com_ptr<ID3D11Device> device;
HRESULT hr = CreateD3DDevice(D3D_DRIVER_TYPE_HARDWARE, device);
if (DXGI_ERROR_UNSUPPORTED == hr) {
hr = CreateD3DDevice(D3D_DRIVER_TYPE_WARP, device);
}
winrt::check_hresult(hr);
return device;
}
inline auto CreateD2DFactory() {
D2D1_FACTORY_OPTIONS options{};
//#ifdef _DEBUG
// options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
//#endif
winrt::com_ptr<ID2D1Factory1> factory;
winrt::check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
options, factory.put()));
return factory;
}
inline auto CreateDXGISwapChain(winrt::com_ptr<ID3D11Device> const &device,
const DXGI_SWAP_CHAIN_DESC1 *desc) {
auto dxgiDevice = device.as<IDXGIDevice2>();
winrt::com_ptr<IDXGIAdapter> adapter;
winrt::check_hresult(dxgiDevice->GetParent(winrt::guid_of<IDXGIAdapter>(),
adapter.put_void()));
winrt::com_ptr<IDXGIFactory2> factory;
winrt::check_hresult(
adapter->GetParent(winrt::guid_of<IDXGIFactory2>(), factory.put_void()));
winrt::com_ptr<IDXGISwapChain1> swapchain;
winrt::check_hresult(factory->CreateSwapChainForComposition(
device.get(), desc, nullptr, swapchain.put()));
return swapchain;
}
inline auto CreateDXGISwapChain(winrt::com_ptr<ID3D11Device> const &device,
uint32_t width, uint32_t height,
DXGI_FORMAT format, uint32_t bufferCount) {
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = width;
desc.Height = height;
desc.Format = format;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferCount = bufferCount;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
return CreateDXGISwapChain(device, &desc);
}

View File

@@ -1,41 +0,0 @@
#pragma once
#include <winrt/windows.graphics.directx.direct3d11.h>
extern "C" {
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
HRESULT __stdcall CreateDirect3D11SurfaceFromDXGISurface(
::IDXGISurface *dgxiSurface, ::IInspectable **graphicsSurface);
}
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
IDirect3DDxgiInterfaceAccess : ::IUnknown {
virtual HRESULT __stdcall GetInterface(GUID const &id, void **object) = 0;
};
inline auto CreateDirect3DDevice(IDXGIDevice *dxgi_device) {
winrt::com_ptr<::IInspectable> d3d_device;
winrt::check_hresult(
CreateDirect3D11DeviceFromDXGIDevice(dxgi_device, d3d_device.put()));
return d3d_device
.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
}
inline auto CreateDirect3DSurface(IDXGISurface *dxgi_surface) {
winrt::com_ptr<::IInspectable> d3d_surface;
winrt::check_hresult(
CreateDirect3D11SurfaceFromDXGISurface(dxgi_surface, d3d_surface.put()));
return d3d_surface
.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>();
}
template <typename T>
auto GetDXGIInterfaceFromObject(
winrt::Windows::Foundation::IInspectable const &object) {
auto access = object.as<IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<T> result;
winrt::check_hresult(
access->GetInterface(winrt::guid_of<T>(), result.put_void()));
return result;
}

View File

@@ -1,160 +0,0 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED <20>AS IS? WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "pch.h"
#include "App.h"
#include "SimpleCapture.h"
#include "Win32MonitorEnumeration.h"
#include "Win32WindowEnumeration.h"
#include <ShObjIdl.h>
using namespace winrt;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Composition::Desktop;
// Direct3D11CaptureFramePool requires a DispatcherQueue
auto CreateDispatcherQueueController() {
namespace abi = ABI::Windows::System;
DispatcherQueueOptions options{sizeof(DispatcherQueueOptions),
DQTYPE_THREAD_CURRENT, DQTAT_COM_STA};
Windows::System::DispatcherQueueController controller{nullptr};
check_hresult(CreateDispatcherQueueController(
options, reinterpret_cast<abi::IDispatcherQueueController **>(
put_abi(controller))));
return controller;
}
DesktopWindowTarget CreateDesktopWindowTarget(Compositor const &compositor,
HWND window) {
namespace abi = ABI::Windows::UI::Composition::Desktop;
auto interop = compositor.as<abi::ICompositorDesktopInterop>();
DesktopWindowTarget target{nullptr};
check_hresult(interop->CreateDesktopWindowTarget(
window, true,
reinterpret_cast<abi::IDesktopWindowTarget **>(put_abi(target))));
return target;
}
int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance,
LPSTR cmdLine, int cmdShow);
auto g_app = std::make_shared<App>();
auto g_windows = EnumerateWindows();
auto g_monitors = EnumerateMonitors();
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance,
LPSTR cmdLine, int cmdShow) {
// Init COM
init_apartment(apartment_type::single_threaded);
// Create the window
WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = instance;
wcex.hIcon = LoadIcon(instance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"ScreenCaptureforHWND";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
WINRT_VERIFY(RegisterClassEx(&wcex));
HWND hwnd = CreateWindow(L"ScreenCaptureforHWND", L"ScreenCaptureforHWND",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
800, 600, NULL, NULL, instance, NULL);
WINRT_VERIFY(hwnd);
ShowWindow(hwnd, cmdShow);
UpdateWindow(hwnd);
// Create combo box
HWND comboBoxHwnd =
CreateWindow(WC_COMBOBOX, L"",
CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_CHILD |
WS_OVERLAPPED | WS_VISIBLE,
10, 10, 200, 200, hwnd, NULL, instance, NULL);
WINRT_VERIFY(comboBoxHwnd);
// Populate combo box
// 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,
(LPARAM)monitor.ClassName().c_str());
}
// SendMessage(comboBoxHwnd, CB_SETCURSEL, 0, 0);
// Create a DispatcherQueue for our thread
auto controller = CreateDispatcherQueueController();
// Initialize Composition
auto compositor = Compositor();
auto target = CreateDesktopWindowTarget(compositor, hwnd);
auto root = compositor.CreateContainerVisual();
root.RelativeSizeAdjustment({1.0f, 1.0f});
target.Root(root);
// Enqueue our capture work on the dispatcher
auto queue = controller.DispatcherQueue();
auto success = queue.TryEnqueue([=]() -> void { g_app->Initialize(root); });
WINRT_VERIFY(success);
// Message pump
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
break;
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()];
auto monitor = g_monitors[0];
g_app->StartCapture(monitor.Hmonitor());
// }
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}

View File

@@ -1 +0,0 @@
#include "pch.h"

View File

@@ -1,34 +0,0 @@
#pragma once
#include <Unknwn.h>
#include <inspectable.h>
// WinRT
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.UI.h>
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Composition.Desktop.h>
#include <winrt/Windows.UI.Popups.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <winrt/Windows.Graphics.DirectX.h>
#include <winrt/Windows.Graphics.DirectX.Direct3d11.h>
#include <windows.ui.composition.interop.h>
#include <DispatcherQueue.h>
// STL
#include <atomic>
#include <memory>
// D3D
#include <d3d11_4.h>
#include <dxgi1_6.h>
#include <d2d1_3.h>
#include <wincodec.h>
// Helpers
#include "composition.interop.h"
#include "d3dHelpers.h"
#include "direct3d11.interop.h"
#include "capture.interop.h"

View File

@@ -1,368 +0,0 @@
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
// Windows
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include "SDL2/SDL.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C" {
#endif
#include <SDL/SDL.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
};
#endif
#endif
// Output YUV420P
#define OUTPUT_YUV420P 0
//'1' Use Dshow
//'0' Use GDIgrab
#define USE_DSHOW 0
// Refresh Event
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)
int thread_exit = 0;
SDL_Texture *sdlTexture = nullptr;
SDL_Renderer *sdlRenderer = nullptr;
SDL_Rect sdlRect;
int YUV420ToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
unsigned char *des_buffer) {
AVFrame *Input_pFrame = av_frame_alloc();
AVFrame *Output_pFrame = av_frame_alloc();
struct SwsContext *img_convert_ctx = sws_getContext(
width, height, AV_PIX_FMT_NV12, width, height, AV_PIX_FMT_YUV420P,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
AV_PIX_FMT_NV12, width, height, 1);
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, des_buffer,
AV_PIX_FMT_YUV420P, width, height, 1);
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
Input_pFrame->linesize, 0, height, Output_pFrame->data,
Output_pFrame->linesize);
if (Input_pFrame) av_free(Input_pFrame);
if (Output_pFrame) av_free(Output_pFrame);
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
return 0;
}
int sfp_refresh_thread(void *opaque) {
thread_exit = 0;
while (!thread_exit) {
SDL_Event event;
event.type = SFM_REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
thread_exit = 0;
// Break
SDL_Event event;
event.type = SFM_BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
// Show Dshow Device
// void show_dshow_device() {
// AVFormatContext *pFormatCtx = avformat_alloc_context();
// AVDictionary *options = NULL;
// av_dict_set(&options, "list_devices", "true", 0);
// AVInputFormat *iformat = av_find_input_format("dshow");
// printf("========Device Info=============\n");
// avformat_open_input(&pFormatCtx, "video=dummy", iformat, &options);
// printf("================================\n");
// }
// Show AVFoundation Device
void show_avfoundation_device() {
const AVFormatContext *const_pFormatCtx = avformat_alloc_context();
AVFormatContext *pFormatCtx = const_cast<AVFormatContext *>(const_pFormatCtx);
AVDictionary *options = NULL;
av_dict_set(&options, "list_devices", "true", 0);
const AVInputFormat *const_iformat = av_find_input_format("avfoundation");
AVInputFormat *iformat = const_cast<AVInputFormat *>(const_iformat);
printf("==AVFoundation Device Info===\n");
avformat_open_input(&pFormatCtx, "", iformat, &options);
printf("=============================\n");
}
int main(int argc, char *argv[]) {
AVFormatContext *pFormatCtx;
int i, videoindex;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
avformat_network_init();
pFormatCtx = avformat_alloc_context();
// Open File
// char filepath[]="src01_480x272_22.h265";
// avformat_open_input(&pFormatCtx,filepath,NULL,NULL)
// Register Device
avdevice_register_all();
// Windows
#ifdef _WIN32
#if USE_DSHOW
// Use dshow
//
// Need to Install screen-capture-recorder
// screen-capture-recorder
// Website: http://sourceforge.net/projects/screencapturer/
//
AVInputFormat *ifmt = av_find_input_format("dshow");
if (avformat_open_input(&pFormatCtx, "video=screen-capture-recorder", ifmt,
NULL) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
#else
// Use gdigrab
AVDictionary *options = NULL;
// Set some options
// grabbing frame rate
// av_dict_set(&options,"framerate","5",0);
// The distance from the left edge of the screen or desktop
// av_dict_set(&options,"offset_x","20",0);
// The distance from the top edge of the screen or desktop
// av_dict_set(&options,"offset_y","40",0);
// Video frame size. The default is to capture the full screen
// av_dict_set(&options,"video_size","640x480",0);
const AVInputFormat *ifmt = av_find_input_format("gdigrab");
if (avformat_open_input(&pFormatCtx, "desktop", ifmt, &options) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
#endif
#elif defined linux
// Linux
AVDictionary *options = NULL;
// Set some options
// grabbing frame rate
// av_dict_set(&options,"framerate","5",0);
// Make the grabbed area follow the mouse
// av_dict_set(&options,"follow_mouse","centered",0);
// Video frame size. The default is to capture the full screen
// av_dict_set(&options,"video_size","640x480",0);
AVInputFormat *ifmt = av_find_input_format("x11grab");
// Grab at position 10,20
if (avformat_open_input(&pFormatCtx, ":0.0+10,20", ifmt, &options) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
#else
show_avfoundation_device();
// Mac
AVInputFormat *ifmt = av_find_input_format("avfoundation");
// Avfoundation
//[video]:[audio]
if (avformat_open_input(&pFormatCtx, "1", ifmt, NULL) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
#endif
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("Couldn't find stream information.\n");
return -1;
}
videoindex = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoindex = i;
break;
}
if (videoindex == -1) {
printf("Didn't find a video stream.\n");
return -1;
}
// pCodecCtx = pFormatCtx->streams[videoindex]->codec;
pCodecCtx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx,
pFormatCtx->streams[videoindex]->codecpar);
pCodec = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx->codec_id));
// pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
printf("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\n");
return -1;
}
AVFrame *pFrame, *pFrameYUV;
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
// unsigned char *out_buffer=(unsigned char
// *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
// pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer,
// AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
// SDL----------------------------
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
const int pixel_w = 640, pixel_h = 360;
int screen_w = 640, screen_h = 360;
// const SDL_VideoInfo *vi = SDL_GetVideoInfo();
// Half of the Desktop's width and height.
screen_w = 640;
screen_h = 360;
// SDL_Surface *screen;
// screen = SDL_SetVideoMode(screen_w, screen_h, 0, 0);
SDL_Window *screen;
screen = SDL_CreateWindow("RTS Receiver", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h,
SDL_WINDOW_RESIZABLE);
if (!screen) {
printf("SDL: could not set video mode - exiting:%s\n", SDL_GetError());
return -1;
}
// SDL_Overlay *bmp;
// bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,
// SDL_YV12_OVERLAY, screen);
sdlRenderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
Uint32 pixformat = 0;
pixformat = SDL_PIXELFORMAT_NV12;
SDL_Texture *sdlTexture = nullptr;
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = screen_w;
rect.h = screen_h;
// SDL End------------------------
int ret, got_picture;
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
#if OUTPUT_YUV420P
FILE *fp_yuv = fopen("output.yuv", "wb+");
#endif
struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getContext(
pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
//------------------------------
SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
//
// SDL_WM_SetCaption("Simplest FFmpeg Grab Desktop", NULL);
// Event Loop
SDL_Event event;
for (;;) {
// Wait
SDL_WaitEvent(&event);
if (event.type == SFM_REFRESH_EVENT) {
//------------------------------
if (av_read_frame(pFormatCtx, packet) >= 0) {
if (packet->stream_index == videoindex) {
avcodec_send_packet(pCodecCtx, packet);
got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
// ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
// packet);
if (ret < 0) {
printf("Decode Error.\n");
return -1;
}
if (got_picture) {
// SDL_LockYUVOverlay(bmp);
// pFrameYUV->data[0] = bmp->pixels[0];
// pFrameYUV->data[1] = bmp->pixels[2];
// pFrameYUV->data[2] = bmp->pixels[1];
// pFrameYUV->linesize[0] = bmp->pitches[0];
// pFrameYUV->linesize[1] = bmp->pitches[2];
// pFrameYUV->linesize[2] = bmp->pitches[1];
// sws_scale(img_convert_ctx,
// (const unsigned char *const *)pFrame->data,
// pFrame->linesize, 0, pCodecCtx->height,
// pFrameYUV->data, pFrameYUV->linesize);
// #if OUTPUT_YUV420P
// int y_size = pCodecCtx->width * pCodecCtx->height;
// fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); // Y
// fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); //
// U fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);
// // V
// #endif
// SDL_UnlockYUVOverlay(bmp);
// SDL_DisplayYUVOverlay(bmp, &rect);
// YUV420ToNV12FFmpeg(buffer, pixel_w, pixel_h, dst_buffer);
// SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
// // FIX: If window is resize
// sdlRect.x = 0;
// sdlRect.y = 0;
// sdlRect.w = screen_w;
// sdlRect.h = screen_h;
// SDL_RenderClear(sdlRenderer);
// SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
// SDL_RenderPresent(sdlRenderer);
}
}
// av_free_packet(packet);
} else {
// Exit Thread
thread_exit = 1;
}
} else if (event.type == SDL_QUIT) {
thread_exit = 1;
} else if (event.type == SFM_BREAK_EVENT) {
break;
}
}
sws_freeContext(img_convert_ctx);
#if OUTPUT_YUV420P
fclose(fp_yuv);
#endif
SDL_Quit();
// av_free(out_buffer);
av_free(pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}

View File

@@ -0,0 +1,12 @@
#include <iostream>
#include "remote_desk_server.h"
int main() {
RemoteDeskServer remote_desk_server;
remote_desk_server.Init();
while (1) {
}
return 0;
}

View File

@@ -0,0 +1,93 @@
#include "remote_desk_server.h"
#include <iostream>
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
};
#define NV12_BUFFER_SIZE 2560 * 1440 * 3 / 2
RemoteDeskServer ::RemoteDeskServer() {}
RemoteDeskServer ::~RemoteDeskServer() {
if (nv12_buffer_) {
delete nv12_buffer_;
nv12_buffer_ = nullptr;
}
}
int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
unsigned char *dst_buffer) {
AVFrame *Input_pFrame = av_frame_alloc();
AVFrame *Output_pFrame = av_frame_alloc();
struct SwsContext *img_convert_ctx = sws_getContext(
width, height, AV_PIX_FMT_BGRA, width, height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
AV_PIX_FMT_BGRA, width, height, 1);
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer,
AV_PIX_FMT_NV12, width, height, 1);
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
Input_pFrame->linesize, 0, height, Output_pFrame->data,
Output_pFrame->linesize);
if (Input_pFrame) av_free(Input_pFrame);
if (Output_pFrame) av_free(Output_pFrame);
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
return 0;
}
void RemoteDeskServer::HostReceiveBuffer(const char *data, size_t size,
const char *user_id,
size_t user_id_size) {
std::string msg(data, size);
std::string user(user_id, user_id_size);
std::cout << "Receive: [" << user << "] " << msg << std::endl;
}
int RemoteDeskServer::Init() {
Params params;
params.cfg_path = "../../../../config/config.ini";
params.on_receive_buffer = [](const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::string msg(data, size);
// std::string user(user_id, user_id_size);
// std::cout << "Receive: [" << user << "] " << msg << std::endl;
};
std::string transmission_id = "000000";
std::string user_id = "Server";
peer = CreatePeer(&params);
CreateConnection(peer, transmission_id.c_str(), user_id.c_str());
nv12_buffer_ = new char[NV12_BUFFER_SIZE];
screen_capture = new ScreenCaptureWgc();
RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = GetSystemMetrics(SM_CXSCREEN);
rect.bottom = GetSystemMetrics(SM_CYSCREEN);
screen_capture->Init(
rect, 60,
[this](unsigned char *data, int size, int width, int height) -> void {
// std::cout << "Send" << std::endl;
BGRAToNV12FFmpeg(data, width, height, (unsigned char *)nv12_buffer_);
SendData(peer, DATA_TYPE::VIDEO, (const char *)nv12_buffer_,
NV12_BUFFER_SIZE);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
});
screen_capture->Start();
return 0;
}

View File

@@ -0,0 +1,25 @@
#ifndef _REMOTE_DESK_SERVER_H_
#define _REMOTE_DESK_SERVER_H_
#include "screen_capture_wgc.h"
#include "x.h"
class RemoteDeskServer {
public:
RemoteDeskServer();
~RemoteDeskServer();
public:
int Init();
static void HostReceiveBuffer(const char* data, size_t size,
const char* user_id, size_t user_id_size);
private:
PeerPtr* peer = nullptr;
ScreenCaptureWgc* screen_capture = nullptr;
char* nv12_buffer_ = nullptr;
};
#endif

View File

@@ -19,9 +19,7 @@ extern "C" {
int screen_w = 2560, screen_h = 1440;
const int pixel_w = 2560, pixel_h = 1440;
unsigned char buffer[pixel_w * pixel_h * 3 / 2];
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
unsigned char rgbData[pixel_w * pixel_h * 4];
SDL_Texture *sdlTexture = nullptr;
SDL_Renderer *sdlRenderer = nullptr;
SDL_Rect sdlRect;

View File

@@ -55,10 +55,7 @@ int ScreenCaptureWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
if (_inited == true) return error;
_fps = fps;
_rect = rect;
_start_time = av_gettime_relative();
_time_base = {1, AV_TIME_BASE};
_pixel_fmt = AV_PIX_FMT_BGRA;
_on_data = cb;
do {
@@ -126,71 +123,9 @@ int ScreenCaptureWgc::Stop() {
}
void ScreenCaptureWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
// std::cout << "onframe" << std::endl;
// AVFrame *av_frame = av_frame_alloc();
// av_frame->pts = av_gettime_relative();
// av_frame->pkt_dts = av_frame->pts;
// // av_frame->pkt_pts = av_frame->pts;
// av_frame->width = frame.width;
// av_frame->height = frame.height;
// av_frame->format = AV_PIX_FMT_BGRA;
// av_frame->pict_type = AV_PICTURE_TYPE_NONE;
// av_frame->pkt_size = frame.width * frame.height * 4;
// 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((unsigned char *)frame.data, frame.width * frame.height * 4,
frame.width, frame.height);
// av_frame_free(&av_frame);
// BGRA to YUV
// auto swrCtxBGRA2YUV = sws_getContext(
// frame.width, frame.height, AV_PIX_FMT_BGRA, frame.width, frame.height,
// AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
// create BGRA
// AVFrame *frame_bgra = av_frame;
// AVFrame *frame_bgra = av_frame_alloc();
// frame_bgra->format = AV_PIX_FMT_BGRA;
// frame_bgra->width = frame.width;
// frame_bgra->height = frame.height;
// if (av_frame_get_buffer(frame_bgra, 32) < 0) {
// printf("Failed: av_frame_get_buffer\n");
// return;
// }
// frame_bgra->data[0] = cropImage;
// YUV
// AVFrame *frame_yuv = av_frame_alloc();
// frame_yuv->width = frame.width;
// frame_yuv->height = frame.height;
// frame_yuv->format = AV_PIX_FMT_YUV420P;
// uint8_t *picture_buf =
// (uint8_t *)av_malloc(frame.width * frame.height * 3 / 2);
// if (av_image_fill_arrays(frame_yuv->data, frame_yuv->linesize, picture_buf,
// AV_PIX_FMT_YUV420P, frame.width, frame.height,
// 1) < 0) {
// std::cout << "Failed: av_image_fill_arrays" << std::endl;
// return;
// }
// if (sws_scale(swrCtxBGRA2YUV, frame_bgra->data, frame_bgra->linesize, 0,
// frame.height, frame_yuv->data, frame_yuv->linesize) < 0) {
// std::cout << "BGRA to YUV failed" << std::endl;
// return;
// }
// frame_yuv->pts = av_gettime();
// if (_on_data)
// _on_data((unsigned char *)frame_yuv->data,
// frame.width * frame.height * 3 / 2, frame.width, frame.height);
}
void ScreenCaptureWgc::CleanUp() {

View File

@@ -3,23 +3,14 @@
#include <Windows.h>
#include "wgc_session.h"
#include "wgc_session_impl.h"
extern "C" {
#include <libavcodec\avcodec.h>
#include <libavfilter\avfilter.h>
#include <libavformat\avformat.h>
#include <libavutil\imgutils.h>
#include <libavutil\time.h>
#include <libswscale/swscale.h>
}
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include "wgc_session.h"
#include "wgc_session_impl.h"
typedef struct {
int left;
int top;
@@ -67,10 +58,6 @@ class ScreenCaptureWgc : public WgcSession::wgc_session_observer {
cb_desktop_data _on_data;
cb_desktop_error _on_error;
AVRational _time_base;
int64_t _start_time;
AVPixelFormat _pixel_fmt;
};
#endif

View File

@@ -1,372 +0,0 @@
/*
* 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

@@ -1,110 +0,0 @@
/*
* 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

@@ -1,136 +0,0 @@
/*
* 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

View File

@@ -1,105 +0,0 @@
/*
* 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

@@ -1,113 +0,0 @@
/*
* 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

View File

@@ -1,218 +0,0 @@
/*
* 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

View File

@@ -1,138 +0,0 @@
/*
* 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

@@ -1,421 +0,0 @@
/*
* 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

@@ -1,19 +0,0 @@
/*
* 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

@@ -1,35 +0,0 @@
/*
* 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

@@ -29,16 +29,30 @@ target("log")
add_headerfiles("../../src/log/log.h")
add_includedirs("../../src/log", {public = true})
target("remote_desk")
set_kind("binary")
add_deps("projectx")
target("screen_capture")
set_kind("static")
add_packages("log")
add_packages("ffmpeg")
add_packages("vcpkg::sdl2")
add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil")
add_files("dll/*.cpp")
add_files("screen_capture/*.cpp")
add_includedirs("screen_capture", {public = true})
target("remote_desk_server")
set_kind("binary")
add_packages("log", "ffmpeg")
add_deps("projectx", "screen_capture")
add_files("remote_desk_server/*.cpp")
add_includedirs("../../src/interface")
add_links("SDL2-static", "SDL2main", "Shell32", "gdi32", "winmm",
"setupapi", "version", "WindowsApp", "Imm32", "avutil")
-- add_links("avformat", "swscale")
-- target("remote_desk")
-- set_kind("binary")
-- add_deps("projectx")
-- add_packages("log")
-- add_packages("ffmpeg")
-- add_packages("vcpkg::sdl2")
-- add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil")
-- add_files("**.cpp")
-- add_includedirs("../../src/interface")
-- add_links("SDL2-static", "SDL2main", "Shell32", "gdi32", "winmm",
-- "setupapi", "version", "WindowsApp", "Imm32", "avutil")