Add wgc demo

This commit is contained in:
dijunkun
2023-08-28 00:53:55 +08:00
parent 69ee2ed5d5
commit 8f803cbd4c
37 changed files with 2561 additions and 6 deletions

View File

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

@@ -0,0 +1,24 @@
#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

@@ -0,0 +1,219 @@
//*********************************************************
//
// 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 "SimpleCapture.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

@@ -0,0 +1,49 @@
#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

@@ -0,0 +1,50 @@
#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

@@ -0,0 +1,101 @@
#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

@@ -0,0 +1,28 @@
#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

@@ -0,0 +1,69 @@
#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

@@ -0,0 +1,132 @@
#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

@@ -0,0 +1,41 @@
#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

@@ -0,0 +1,159 @@
//*********************************************************
//
// 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()];
g_app->StartCapture(monitor.Hmonitor());
}
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}

View File

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

View File

@@ -0,0 +1,34 @@
#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

@@ -0,0 +1,19 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

View File

@@ -0,0 +1,218 @@
#ifndef ERROR_DEFINE
#define ERROR_DEFINE
enum AM_ERROR{
AE_NO = 0,
AE_ERROR,
AE_UNSUPPORT,
AE_INVALID_CONTEXT,
AE_NEED_INIT,
AE_TIMEOUT,
AE_ALLOCATE_FAILED,
//AE_CO_
AE_CO_INITED_FAILED,
AE_CO_CREATE_FAILED,
AE_CO_GETENDPOINT_FAILED,
AE_CO_ACTIVE_DEVICE_FAILED,
AE_CO_GET_FORMAT_FAILED,
AE_CO_AUDIOCLIENT_INIT_FAILED,
AE_CO_GET_CAPTURE_FAILED,
AE_CO_CREATE_EVENT_FAILED,
AE_CO_SET_EVENT_FAILED,
AE_CO_START_FAILED,
AE_CO_ENUMENDPOINT_FAILED,
AE_CO_GET_ENDPOINT_COUNT_FAILED,
AE_CO_GET_ENDPOINT_ID_FAILED,
AE_CO_OPEN_PROPERTY_FAILED,
AE_CO_GET_VALUE_FAILED,
AE_CO_GET_BUFFER_FAILED,
AE_CO_RELEASE_BUFFER_FAILED,
AE_CO_GET_PACKET_FAILED,
AE_CO_PADDING_UNEXPECTED,
//AE_FFMPEG_
AE_FFMPEG_OPEN_INPUT_FAILED,
AE_FFMPEG_FIND_STREAM_FAILED,
AE_FFMPEG_FIND_DECODER_FAILED,
AE_FFMPEG_OPEN_CODEC_FAILED,
AE_FFMPEG_READ_FRAME_FAILED,
AE_FFMPEG_READ_PACKET_FAILED,
AE_FFMPEG_DECODE_FRAME_FAILED,
AE_FFMPEG_NEW_SWSCALE_FAILED,
AE_FFMPEG_FIND_ENCODER_FAILED,
AE_FFMPEG_ALLOC_CONTEXT_FAILED,
AE_FFMPEG_ENCODE_FRAME_FAILED,
AE_FFMPEG_ALLOC_FRAME_FAILED,
AE_FFMPEG_OPEN_IO_FAILED,
AE_FFMPEG_CREATE_STREAM_FAILED,
AE_FFMPEG_COPY_PARAMS_FAILED,
AE_RESAMPLE_INIT_FAILED,
AE_FFMPEG_NEW_STREAM_FAILED,
AE_FFMPEG_FIND_INPUT_FMT_FAILED,
AE_FFMPEG_WRITE_HEADER_FAILED,
AE_FFMPEG_WRITE_TRAILER_FAILED,
AE_FFMPEG_WRITE_FRAME_FAILED,
//AE_FILTER_
AE_FILTER_ALLOC_GRAPH_FAILED,
AE_FILTER_CREATE_FILTER_FAILED,
AE_FILTER_PARSE_PTR_FAILED,
AE_FILTER_CONFIG_FAILED,
AE_FILTER_INVALID_CTX_INDEX,
AE_FILTER_ADD_FRAME_FAILED,
//AE_GDI_
AE_GDI_GET_DC_FAILED,
AE_GDI_CREATE_DC_FAILED,
AE_GDI_CREATE_BMP_FAILED,
AE_GDI_BITBLT_FAILED,
AE_GDI_GET_DIBITS_FAILED,
//AE_D3D_
AE_D3D_LOAD_FAILED,
AE_D3D_GET_PROC_FAILED,
AE_D3D_CREATE_DEVICE_FAILED,
AE_D3D_QUERYINTERFACE_FAILED,
AE_D3D_CREATE_VERTEX_SHADER_FAILED,
AE_D3D_CREATE_INLAYOUT_FAILED,
AE_D3D_CREATE_PIXEL_SHADER_FAILED,
AE_D3D_CREATE_SAMPLERSTATE_FAILED,
//AE_DXGI_
AE_DXGI_GET_PROC_FAILED,
AE_DXGI_GET_ADAPTER_FAILED,
AE_DXGI_GET_FACTORY_FAILED,
AE_DXGI_FOUND_ADAPTER_FAILED,
//AE_DUP_
AE_DUP_ATTATCH_FAILED,
AE_DUP_QI_FAILED,
AE_DUP_GET_PARENT_FAILED,
AE_DUP_ENUM_OUTPUT_FAILED,
AE_DUP_DUPLICATE_MAX_FAILED,
AE_DUP_DUPLICATE_FAILED,
AE_DUP_RELEASE_FRAME_FAILED,
AE_DUP_ACQUIRE_FRAME_FAILED,
AE_DUP_QI_FRAME_FAILED,
AE_DUP_CREATE_TEXTURE_FAILED,
AE_DUP_QI_DXGI_FAILED,
AE_DUP_MAP_FAILED,
AE_DUP_GET_CURSORSHAPE_FAILED,
//AE_REMUX_
AE_REMUX_RUNNING,
AE_REMUX_NOT_EXIST,
AE_REMUX_INVALID_INOUT,
// AE_WGC_
AE_WGC_CREATE_CAPTURER_FAILED,
AE_MAX
};
static const char *ERRORS_STR[] = {
"no error", //AE_NO
"error", //AE_ERROR
"not support for now", //AE_UNSUPPORT
"invalid context", //AE_INVALID_CONTEXT
"need init first", //AE_NEED_INIT
"operation timeout", //AE_TIMEOUT
"allocate memory failed", //AE_ALLOCATE_FAILED,
"com init failed", //AE_CO_INITED_FAILED
"com create instance failed", //AE_CO_CREATE_FAILED
"com get endpoint failed", //AE_CO_GETENDPOINT_FAILED
"com active device failed", //AE_CO_ACTIVE_DEVICE_FAILED
"com get wave formatex failed", //AE_CO_GET_FORMAT_FAILED
"com audio client init failed", //AE_CO_AUDIOCLIENT_INIT_FAILED
"com audio get capture failed", //AE_CO_GET_CAPTURE_FAILED
"com audio create event failed", //AE_CO_CREATE_EVENT_FAILED
"com set ready event failed", //AE_CO_SET_EVENT_FAILED
"com start to record failed", //AE_CO_START_FAILED
"com enum audio endpoints failed", //AE_CO_ENUMENDPOINT_FAILED
"com get endpoints count failed", //AE_CO_GET_ENDPOINT_COUNT_FAILED
"com get endpoint id failed", //AE_CO_GET_ENDPOINT_ID_FAILED
"com open endpoint property failed", //AE_CO_OPEN_PROPERTY_FAILED
"com get property value failed", //AE_CO_GET_VALUE_FAILED
"com get buffer failed", //AE_CO_GET_BUFFER_FAILED
"com release buffer failed", //AE_CO_RELEASE_BUFFER_FAILED
"com get packet size failed", //AE_CO_GET_PACKET_FAILED
"com get padding size unexpected", //AE_CO_PADDING_UNEXPECTED
"ffmpeg open input failed", //AE_FFMPEG_OPEN_INPUT_FAILED
"ffmpeg find stream info failed", //AE_FFMPEG_FIND_STREAM_FAILED
"ffmpeg find decoder failed", //AE_FFMPEG_FIND_DECODER_FAILED
"ffmpeg open codec failed", //AE_FFMPEG_OPEN_CODEC_FAILED
"ffmpeg read frame failed", //AE_FFMPEG_READ_FRAME_FAILED
"ffmpeg read packet failed", //AE_FFMPEG_READ_PACKET_FAILED
"ffmpeg decode frame failed", //AE_FFMPEG_DECODE_FRAME_FAILED
"ffmpeg create swscale failed", //AE_FFMPEG_NEW_SWSCALE_FAILED
"ffmpeg find encoder failed", //AE_FFMPEG_FIND_ENCODER_FAILED
"ffmpeg alloc context failed", //AE_FFMPEG_ALLOC_CONTEXT_FAILED
"ffmpeg encode frame failed", //AE_FFMPEG_ENCODE_FRAME_FAILED
"ffmpeg alloc frame failed", //AE_FFMPEG_ALLOC_FRAME_FAILED
"ffmpeg open io ctx failed", //AE_FFMPEG_OPEN_IO_FAILED
"ffmpeg new stream failed", //AE_FFMPEG_CREATE_STREAM_FAILED
"ffmpeg copy parameters failed", //AE_FFMPEG_COPY_PARAMS_FAILED
"resampler init failed", //AE_RESAMPLE_INIT_FAILED
"ffmpeg new out stream failed", //AE_FFMPEG_NEW_STREAM_FAILED
"ffmpeg find input format failed", //AE_FFMPEG_FIND_INPUT_FMT_FAILED
"ffmpeg write file header failed", //AE_FFMPEG_WRITE_HEADER_FAILED
"ffmpeg write file trailer failed", //AE_FFMPEG_WRITE_TRAILER_FAILED
"ffmpeg write frame failed", //AE_FFMPEG_WRITE_FRAME_FAILED
"avfilter alloc avfilter failed", //AE_FILTER_ALLOC_GRAPH_FAILED
"avfilter create graph failed", //AE_FILTER_CREATE_FILTER_FAILED
"avfilter parse ptr failed", //AE_FILTER_PARSE_PTR_FAILED
"avfilter config graph failed", //AE_FILTER_CONFIG_FAILED
"avfilter invalid ctx index", //AE_FILTER_INVALID_CTX_INDEX
"avfilter add frame failed", //AE_FILTER_ADD_FRAME_FAILED
"gdi get dc failed", //AE_GDI_GET_DC_FAILED
"gdi create dc failed", //AE_GDI_CREATE_DC_FAILED
"gdi create bmp failed", //AE_GDI_CREATE_BMP_FAILED
"gdi bitblt failed", //AE_GDI_BITBLT_FAILED
"gid geet dibbits failed", //AE_GDI_GET_DIBITS_FAILED
"d3d11 library load failed", //AE_D3D_LOAD_FAILED
"d3d11 proc get failed", //AE_D3D_GET_PROC_FAILED
"d3d11 create device failed", //AE_D3D_CREATE_DEVICE_FAILED
"d3d11 query interface failed", //AE_D3D_QUERYINTERFACE_FAILED
"d3d11 create vertex shader failed",//AE_D3D_CREATE_VERTEX_SHADER_FAILED
"d3d11 create input layout failed", //AE_D3D_CREATE_INLAYOUT_FAILED
"d3d11 create pixel shader failed", //AE_D3D_CREATE_PIXEL_SHADER_FAILED
"d3d11 create sampler state failed",//AE_D3D_CREATE_SAMPLERSTATE_FAILED
"dxgi get proc address failed", //AE_DXGI_GET_PROC_FAILED
"dxgi get adapter failed", //AE_DXGI_GET_ADAPTER_FAILED
"dxgi get factory failed", //AE_DXGI_GET_FACTORY_FAILED
"dxgi specified adapter not found", //AE_DXGI_FOUND_ADAPTER_FAILED
"duplication attatch desktop failed", //AE_DUP_ATTATCH_FAILED
"duplication query interface failed", //AE_DUP_QI_FAILED
"duplication get parent failed", //AE_DUP_GET_PARENT_FAILED
"duplication enum ouput failed", //AE_DUP_ENUM_OUTPUT_FAILED
"duplication duplicate unavailable", //AE_DUP_DUPLICATE_MAX_FAILED
"duplication duplicate failed", //AE_DUP_DUPLICATE_FAILED
"duplication release frame failed", //AE_DUP_RELEASE_FRAME_FAILED
"duplication acquire frame failed", //AE_DUP_ACQUIRE_FRAME_FAILED
"duplication qi frame failed", //AE_DUP_QI_FRAME_FAILED
"duplication create texture failed", //AE_DUP_CREATE_TEXTURE_FAILED
"duplication dxgi qi failed", //AE_DUP_QI_DXGI_FAILED
"duplication map rects failed", //AE_DUP_MAP_FAILED
"duplication get cursor shape failed",//AE_DUP_GET_CURSORSHAPE_FAILED
"remux is already running", //AE_REMUX_RUNNING
"remux input file do not exist", //AE_REMUX_NOT_EXIST
"remux input or output file invalid", //AE_REMUX_INVALID_INOUT
};
#define err2str(e) e < AE_MAX ? ERRORS_STR[e] : "unknown"
#define AMERROR_CHECK(err) if(err != AE_NO) return err
#endif // !ERROR_DEFINE

View File

@@ -0,0 +1,17 @@
#include "pch.h"
#include <winrt/Windows.Foundation.Metadata.h>
bool wgc_is_supported() {
try {
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
return winrt::Windows::Foundation::Metadata::ApiInformation::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
} catch (const winrt::hresult_error &) {
return false;
} catch (...) {
return false;
}
}
am::wgc_session *wgc_create_session() { return new am::wgc_session_impl(); }

View File

@@ -0,0 +1,50 @@
#pragma once
#include <Windows.h>
#ifdef AMRECORDER_IMPORT
#define AMRECORDER_API extern "C" __declspec(dllimport)
#else
#define AMRECORDER_API extern "C" __declspec(dllexport)
#endif
namespace am {
class wgc_session {
public:
struct wgc_session_frame {
unsigned int width;
unsigned int height;
unsigned int row_pitch;
const unsigned char *data;
};
class wgc_session_observer {
public:
virtual ~wgc_session_observer() {}
virtual void on_frame(const wgc_session_frame &frame) = 0;
};
public:
virtual void release() = 0;
virtual int initialize(HWND hwnd) = 0;
virtual int initialize(HMONITOR hmonitor) = 0;
virtual void register_observer(wgc_session_observer *observer) = 0;
virtual int start() = 0;
virtual int stop() = 0;
virtual int pause() = 0;
virtual int resume() = 0;
protected:
virtual ~wgc_session(){};
};
} // namespace am
AMRECORDER_API bool wgc_is_supported();
AMRECORDER_API am::wgc_session *wgc_create_session();

View File

@@ -0,0 +1,21 @@
#ifndef _HEAD_H_
#define _HEAD_H_
#include "record_desktop.h"
class rd : public am::record_desktop {
public:
rd(){};
virtual ~rd(){};
int init(const RECORD_DESKTOP_RECT &rect, const int fps) { return 0; };
int start() { return 0; };
int pause() { return 0; };
int resume() { return 0; };
int stop() { return 0; };
void clean_up() {}
};
#endif

View File

@@ -0,0 +1,22 @@
#pragma once
extern "C" {
#include <libavcodec\adts_parser.h>
#include <libavcodec\avcodec.h>
#include <libavdevice\avdevice.h>
#include <libavfilter\avfilter.h>
#include <libavfilter\buffersink.h>
#include <libavfilter\buffersrc.h>
#include <libavformat\avformat.h>
#include <libavutil\avassert.h>
#include <libavutil\channel_layout.h>
#include <libavutil\error.h>
#include <libavutil\imgutils.h>
#include <libavutil\log.h>
#include <libavutil\mathematics.h>
#include <libavutil\opt.h>
#include <libavutil\samplefmt.h>
#include <libavutil\time.h>
#include <libavutil\timestamp.h>
#include <libswresample\swresample.h>
#include <libswscale\swscale.h>
}

View File

@@ -0,0 +1,60 @@
#include "log_helper.h"
#include <share.h>
#include <stdarg.h>
#include <stdio.h>
#include <mutex>
#define AMLOCK(A) std::lock_guard<std::mutex> lock(A)
#define LOG_ROLL_SIZE (1024 * 1024)
AMLog* AMLog::_log = NULL;
std::mutex _lock;
AMLog::AMLog(FILE* handle) : _handle(handle) { _log = this; }
AMLog::~AMLog() {
AMLOCK(_lock);
if (_log && _handle) {
fclose(_handle);
_log = NULL;
}
}
AMLog* AMLog::get(const char* path) {
if (_log || !path) {
return _log;
}
// DWORD size = 0;
// HANDLE file = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
// FILE_ATTRIBUTE_NORMAL, NULL); if (file != INVALID_HANDLE_VALUE) {
// size = GetFileSize(file, NULL); CloseHandle(file);
// }
// if (size != INVALID_FILE_SIZE && size > LOG_ROLL_SIZE) {
// if (DeleteFileA(path) == FALSE) {
// TCHAR roll_path[MAX_PATH];
// sprintf_s(roll_path, MAX_PATH, "%s.1", path);
// if (!MoveFileEx(path, roll_path, MOVEFILE_REPLACE_EXISTING)) {
// return NULL;
// }
// }
// }
// FILE* handle = _fsopen(path, "a+", _SH_DENYNO);
// if (!handle) {
// return NULL;
// }
// _log = new AMLog(handle);
return _log;
}
void AMLog::printf(const char* format, ...) {
AMLOCK(_lock);
va_list args;
va_start(args, format);
vfprintf(_handle, format, args);
va_end(args);
fflush(_handle);
}

View File

@@ -0,0 +1,63 @@
#ifndef AM_LOG
#define AM_LOG
#include <stdio.h>
#include <time.h>
#include <sys\timeb.h>
#include <windows.h>
class AMLog {
public:
~AMLog();
static AMLog* get(const char* path = NULL);
void printf(const char* format, ...);
private:
AMLog(FILE* handle);
private:
static AMLog* _log;
FILE* _handle;
};
enum AM_LOG_TYPE {
AL_TYPE_DEBUG = 0,
AL_TYPE_INFO,
AL_TYPE_WARN,
AL_TYPE_ERROR,
AL_TYPE_FATAL,
};
static const char *AM_LOG_STR[] = { "DEBUG", "INFO", "WARN", "ERROR", "FATAL" };
#define al_printf(type,format,datetime,ms,...) \
printf("%s-%.3d [%s] [%s(%d)] " format "\n", datetime,ms,type, __FUNCTION__,__LINE__, ## __VA_ARGS__)
#define PRINT_LINE(type, format, datetime, ms, ...) \
printf("%s-%.3d [%s] [%s(%d)] " format "\n", datetime,ms,type, __FUNCTION__,__LINE__, ## __VA_ARGS__)
#define al_log(type,format,...) do{ \
struct _timeb now; \
struct tm today; \
char datetime_str[20]; \
_ftime_s(&now); \
localtime_s(&today, &now.time); \
strftime(datetime_str, 20, "%Y-%m-%d %H:%M:%S", &today); \
AMLog *am_log = AMLog::get(); \
if(am_log){ \
am_log->PRINT_LINE(AM_LOG_STR[type], format, datetime_str, now.millitm, ## __VA_ARGS__); \
} else { \
al_printf(AM_LOG_STR[type], format, datetime_str, now.millitm, ## __VA_ARGS__); \
} \
}while (0)
#define al_debug(format, ...) al_log(AL_TYPE_DEBUG, format, ## __VA_ARGS__)
#define al_info(format, ...) al_log(AL_TYPE_INFO, format, ## __VA_ARGS__)
#define al_warn(format, ...) al_log(AL_TYPE_WARN, format, ## __VA_ARGS__)
#define al_error(format, ...) al_log(AL_TYPE_ERROR, format, ## __VA_ARGS__)
#define al_fatal(format, ...) al_log(AL_TYPE_FATAL, format, ## __VA_ARGS__)
#endif

View File

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

View File

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

View File

@@ -0,0 +1,48 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for
// future builds. This also affects IntelliSense performance, including code
// completion and many code browsing features. However, files listed here are
// ALL re-compiled if any one of them is updated between builds. Do not add
// files here that you will be updating frequently as this negates the
// performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <Unknwn.h>
#include <inspectable.h>
// WinRT
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Graphics.DirectX.h>
#include <winrt/Windows.Graphics.DirectX.Direct3d11.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <Windows.Graphics.Capture.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>
// windowws
#include <Windows.h>
#include "error_define.h"
#include "export.h"
#include "wgc_session_impl.h"
#endif // PCH_H

View File

@@ -0,0 +1,23 @@
#include "record_desktop.h"
am::record_desktop::record_desktop()
{
_running = false;
_paused = false;
_inited = false;
_on_data = nullptr;
_on_error = nullptr;
_device_name = "";
_data_type = RECORD_DESKTOP_DATA_TYPES::AT_DESKTOP_BGRA;
_time_base = { 1,AV_TIME_BASE };
_start_time = 0;
_pixel_fmt = AV_PIX_FMT_NONE;
}
am::record_desktop::~record_desktop()
{
}

View File

@@ -0,0 +1,80 @@
#ifndef RECORD_DESKTOP
#define RECORD_DESKTOP
#include "record_desktop_define.h"
#include "headers_ffmpeg.h"
#include <atomic>
#include <thread>
#include <functional>
#include <string>
namespace am {
typedef std::function<void(AVFrame *frame)> cb_desktop_data;
typedef std::function<void(int)> cb_desktop_error;
class record_desktop
{
public:
record_desktop();
virtual ~record_desktop();
virtual int init(
const RECORD_DESKTOP_RECT &rect,
const int fps
) = 0;
virtual int start() = 0;
virtual int pause() = 0;
virtual int resume() = 0;
virtual int stop() = 0;
inline const AVRational & get_time_base() { return _time_base; }
inline int64_t get_start_time() { return _start_time; }
inline AVPixelFormat get_pixel_fmt() { return _pixel_fmt; }
public:
inline bool is_recording() { return _running; }
inline const std::string & get_device_name() { return _device_name; }
inline const RECORD_DESKTOP_DATA_TYPES get_data_type() { return _data_type; }
inline void registe_cb(
cb_desktop_data on_data,
cb_desktop_error on_error) {
_on_data = on_data;
_on_error = on_error;
}
inline const RECORD_DESKTOP_RECT & get_rect() { return _rect; }
inline const int get_frame_rate() { return _fps; }
protected:
virtual void clean_up() = 0;
protected:
std::atomic_bool _running;
std::atomic_bool _paused;
std::atomic_bool _inited;
std::thread _thread;
std::string _device_name;
RECORD_DESKTOP_RECT _rect;
RECORD_DESKTOP_DATA_TYPES _data_type;
int _fps;
cb_desktop_data _on_data;
cb_desktop_error _on_error;
AVRational _time_base;
int64_t _start_time;
AVPixelFormat _pixel_fmt;
};
}
#endif

View File

@@ -0,0 +1,45 @@
#ifndef RECORD_DESKTOP_DEFINE
#define RECORD_DESKTOP_DEFINE
/*
* Record typee
*
*/
typedef enum {
DT_DESKTOP_NO = 0,
DT_DESKTOP_FFMPEG_GDI,
DT_DESKTOP_FFMPEG_DSHOW,
DT_DESKTOP_WIN_GDI,
DT_DESKTOP_WIN_DUPLICATION,
DT_DESKTOP_WIN_WGC,
DT_DESKTOP_WIN_MAG
}RECORD_DESKTOP_TYPES;
/*
* Record desktop data type
*
*/
typedef enum {
AT_DESKTOP_NO = 0,
AT_DESKTOP_RGBA,
AT_DESKTOP_BGRA
}RECORD_DESKTOP_DATA_TYPES;
/**
* Record desktop rect
*
*/
typedef struct {
int left;
int top;
int right;
int bottom;
}RECORD_DESKTOP_RECT;
#endif

View File

@@ -0,0 +1,144 @@
#include "record_desktop_wgc.h"
#include "error_define.h"
#include "log_helper.h"
#include "system_error.h"
#include "utils_string.h"
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
LPARAM data) {
MONITORINFOEX info_ex;
info_ex.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hmonitor, &info_ex);
if (info_ex.dwFlags == DISPLAY_DEVICE_MIRRORING_DRIVER) return true;
if (info_ex.dwFlags & MONITORINFOF_PRIMARY) {
*(HMONITOR *)data = hmonitor;
}
return true;
}
HMONITOR GetPrimaryMonitor() {
HMONITOR hmonitor = nullptr;
::EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&hmonitor);
return hmonitor;
}
namespace am {
record_desktop_wgc::record_desktop_wgc() {}
record_desktop_wgc::~record_desktop_wgc() {
stop();
clean_up();
}
int record_desktop_wgc::init(const RECORD_DESKTOP_RECT &rect, const int fps) {
int error = AE_NO;
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;
do {
if (!module_.is_supported()) {
error = AE_UNSUPPORT;
break;
}
session_ = module_.create_session();
if (!session_) {
error = AE_WGC_CREATE_CAPTURER_FAILED;
break;
}
session_->register_observer(this);
error = session_->initialize(GetPrimaryMonitor());
_inited = true;
} while (0);
if (error != AE_NO) {
al_debug("%s,last error:%s", err2str(error),
system_error::error2str(GetLastError()).c_str());
}
return error;
}
int record_desktop_wgc::start() {
if (_running == true) {
al_warn("record desktop duplication is already running");
return AE_NO;
}
if (_inited == false) {
return AE_NEED_INIT;
}
_running = true;
session_->start();
return AE_NO;
}
int record_desktop_wgc::pause() {
_paused = true;
if (session_) session_->pause();
return AE_NO;
}
int record_desktop_wgc::resume() {
_paused = false;
if (session_) session_->resume();
return AE_NO;
}
int record_desktop_wgc::stop() {
_running = false;
if (session_) session_->stop();
return AE_NO;
}
void record_desktop_wgc::on_frame(const wgc_session::wgc_session_frame &frame) {
al_debug("wgc on frame");
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(av_frame);
av_frame_free(&av_frame);
}
void record_desktop_wgc::clean_up() {
_inited = false;
if (session_) session_->release();
session_ = nullptr;
}
} // namespace am

View File

@@ -0,0 +1,65 @@
#pragma once
#include <Windows.h>
#include "export.h"
#include "record_desktop.h"
namespace am {
class record_desktop_wgc : public record_desktop,
public wgc_session::wgc_session_observer {
class wgc_session_module {
using func_type_is_supported = bool (*)();
using func_type_create_session = wgc_session *(*)();
public:
wgc_session_module() {
module_ = ::LoadLibraryA("WGC.dll");
if (module_) {
func_is_supported_ = (func_type_is_supported)::GetProcAddress(
module_, "wgc_is_supported");
func_create_session_ = (func_type_create_session)::GetProcAddress(
module_, "wgc_create_session");
}
}
~wgc_session_module() {
if (module_) ::FreeModule(module_);
}
bool is_supported() const {
return func_create_session_ && func_is_supported_();
}
wgc_session *create_session() const {
if (!func_create_session_) return nullptr;
return func_create_session_();
}
private:
HMODULE module_ = nullptr;
func_type_is_supported func_is_supported_ = nullptr;
func_type_create_session func_create_session_ = nullptr;
};
public:
record_desktop_wgc();
~record_desktop_wgc();
int init(const RECORD_DESKTOP_RECT &rect, const int fps) override;
int start() override;
int pause() override;
int resume() override;
int stop() override;
void on_frame(const wgc_session::wgc_session_frame &frame) override;
protected:
void clean_up() override;
private:
wgc_session *session_ = nullptr;
wgc_session_module module_;
};
} // namespace am

View File

@@ -0,0 +1,39 @@
#include "system_error.h"
#include <Windows.h>
namespace am {
const std::string& system_error::error2str(unsigned long error) {
// DWORD system_locale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
// HLOCAL local_buf = nullptr;
// BOOL ret = FormatMessage(
// FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
// FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, system_locale,(PSTR)
// &local_buf, 0, NULL);
// if (!ret) {
// HMODULE hnetmsg = LoadLibraryEx("netmsg.dll", NULL,
// DONT_RESOLVE_DLL_REFERENCES); if (hnetmsg != nullptr) {
// ret = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE |
// FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
// hnetmsg, error, system_locale, (PSTR)&local_buf, 0, NULL);
// FreeLibrary(hnetmsg);
// }
// }
// std::string error_str;
// if (ret) {
// error_str = (LPCTSTR)LocalLock(local_buf);
// LocalFree(local_buf);
// }
// return error_str;
return "";
}
} // namespace am

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
namespace am {
class system_error {
public:
static const std::string& error2str(unsigned long error);
};
}

View File

@@ -0,0 +1,66 @@
#include "utils_string.h"
#include <Windows.h>
#ifdef WIN32
#include <Windows.h>
#endif
namespace am {
std::wstring utils_string::ascii_unicode(const std::string &str) {
int unicodeLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
wchar_t *pUnicode = (wchar_t *)malloc(sizeof(wchar_t) * unicodeLen);
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, pUnicode, unicodeLen);
std::wstring ret_str = pUnicode;
free(pUnicode);
return ret_str;
}
std::string utils_string::unicode_ascii(const std::wstring &wstr) {
int ansiiLen = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, nullptr, 0,
nullptr, nullptr);
char *pAssii = (char *)malloc(sizeof(char) * ansiiLen);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, pAssii, ansiiLen, nullptr,
nullptr);
std::string ret_str = pAssii;
free(pAssii);
return ret_str;
}
std::string utils_string::ascii_utf8(const std::string &str) {
return unicode_utf8(ascii_unicode(str));
}
std::string utils_string::utf8_ascii(const std::string &utf8) {
return unicode_ascii(utf8_unicode(utf8));
}
std::string utils_string::unicode_utf8(const std::wstring &wstr) {
int ansiiLen = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0,
nullptr, nullptr);
char *pAssii = (char *)malloc(sizeof(char) * ansiiLen);
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, pAssii, ansiiLen, nullptr,
nullptr);
std::string ret_str = pAssii;
free(pAssii);
return ret_str;
}
std::wstring utils_string::utf8_unicode(const std::string &utf8) {
int unicodeLen =
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
wchar_t *pUnicode = (wchar_t *)malloc(sizeof(wchar_t) * unicodeLen);
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, pUnicode, unicodeLen);
std::wstring ret_str = pUnicode;
free(pUnicode);
return ret_str;
}
} // namespace am

View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
namespace am {
class utils_string
{
public:
static std::wstring ascii_unicode(const std::string & str);
static std::string unicode_ascii(const std::wstring &wstr);
static std::string ascii_utf8(const std::string & str);
static std::string utf8_ascii(const std::string &utf8);
static std::string unicode_utf8(const std::wstring& wstr);
static std::wstring utf8_unicode(const std::string &utf8);
};
}

View File

@@ -0,0 +1,425 @@
#include "pch.h"
#include <functional>
#include <memory>
#define CHECK_INIT \
if (!is_initialized_) \
return AM_ERROR::AE_NEED_INIT
#define CHECK_CLOSED \
if (cleaned_.load() == true) { \
throw winrt::hresult_error(RO_E_CLOSED); \
}
extern "C" {
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
}
namespace am {
wgc_session_impl::wgc_session_impl() {}
wgc_session_impl::~wgc_session_impl() {
stop();
cleanup();
}
void wgc_session_impl::release() { delete this; }
int wgc_session_impl::initialize(HWND hwnd) {
std::lock_guard locker(lock_);
target_.hwnd = hwnd;
target_.is_window = true;
return initialize();
}
int wgc_session_impl::initialize(HMONITOR hmonitor) {
std::lock_guard locker(lock_);
target_.hmonitor = hmonitor;
target_.is_window = false;
return initialize();
}
void wgc_session_impl::register_observer(wgc_session_observer *observer) {
std::lock_guard locker(lock_);
observer_ = observer;
}
int wgc_session_impl::start() {
std::lock_guard locker(lock_);
if (is_running_)
return AM_ERROR::AE_NO;
int error = AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
CHECK_INIT;
try {
if (!capture_session_) {
auto current_size = capture_item_.Size();
capture_framepool_ =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
CreateFreeThreaded(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &wgc_session_impl::on_frame});
capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &wgc_session_impl::on_closed});
}
if (!capture_framepool_)
throw std::exception();
is_running_ = true;
// we do not need to crate a thread to enter a message loop coz we use
// CreateFreeThreaded instead of Create to create a capture frame pool,
// we need to test the performance later
// loop_ = std::thread(std::bind(&wgc_session_impl::message_func, this));
capture_session_.StartCapture();
error = AM_ERROR::AE_NO;
} catch (winrt::hresult_error) {
return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
} catch (...) {
return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
}
return error;
}
int wgc_session_impl::stop() {
std::lock_guard locker(lock_);
CHECK_INIT;
is_running_ = false;
if (loop_.joinable())
loop_.join();
if (capture_framepool_trigger_)
capture_framepool_trigger_.revoke();
if (capture_session_) {
capture_session_.Close();
capture_session_ = nullptr;
}
return AM_ERROR::AE_NO;
}
int wgc_session_impl::pause() {
std::lock_guard locker(lock_);
CHECK_INIT;
return AM_ERROR::AE_NO;
}
int wgc_session_impl::resume() {
std::lock_guard locker(lock_);
CHECK_INIT;
return AM_ERROR::AE_NO;
}
auto wgc_session_impl::create_d3d11_device() {
auto create_d3d_device = [](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);
};
auto create_d3d_device_wrapper = [&create_d3d_device]() {
winrt::com_ptr<ID3D11Device> device;
HRESULT hr = create_d3d_device(D3D_DRIVER_TYPE_HARDWARE, device);
if (DXGI_ERROR_UNSUPPORTED == hr) {
hr = create_d3d_device(D3D_DRIVER_TYPE_WARP, device);
}
winrt::check_hresult(hr);
return device;
};
auto d3d_device = create_d3d_device_wrapper();
auto dxgi_device = d3d_device.as<IDXGIDevice>();
winrt::com_ptr<::IInspectable> d3d11_device;
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(
dxgi_device.get(), d3d11_device.put()));
return d3d11_device
.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
}
auto wgc_session_impl::create_capture_item(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;
}
auto wgc_session_impl::create_capture_item(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;
}
HRESULT wgc_session_impl::create_mapped_texture(
winrt::com_ptr<ID3D11Texture2D> src_texture, unsigned int width,
unsigned int 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 = get_dxgi_interface<ID3D11Device>(d3d11_direct_device_);
return d3dDevice->CreateTexture2D(&map_desc, nullptr,
d3d11_texture_mapped_.put());
}
void wgc_session_impl::on_frame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,
winrt::Windows::Foundation::IInspectable const &args) {
std::lock_guard locker(lock_);
auto is_new_size = false;
{
auto frame = sender.TryGetNextFrame();
auto frame_size = frame.ContentSize();
if (frame_size.Width != capture_frame_size_.Width ||
frame_size.Height != capture_frame_size_.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.
is_new_size = true;
capture_frame_size_ = frame_size;
}
// copy to mapped texture
{
auto frame_captured =
get_dxgi_interface<ID3D11Texture2D>(frame.Surface());
if (!d3d11_texture_mapped_ || is_new_size)
create_mapped_texture(frame_captured);
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
frame_captured.get());
D3D11_MAPPED_SUBRESOURCE map_result;
HRESULT hr = d3d11_device_context_->Map(
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
0 /*coz we use CreateFreeThreaded, so we cant use flags
D3D11_MAP_FLAG_DO_NOT_WAIT*/
,
&map_result);
if (FAILED(hr)) {
OutputDebugStringW(
(L"map resource failed: " + std::to_wstring(hr)).c_str());
}
// copy data from map_result.pData
if (map_result.pData && observer_) {
observer_->on_frame(wgc_session_frame{
static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height), map_result.RowPitch,
const_cast<const unsigned char *>(
(unsigned char *)map_result.pData)});
}
#if 0
if (map_result.pData) {
static unsigned char *buffer = nullptr;
if (buffer && is_new_size)
delete[] buffer;
if (!buffer)
buffer = new unsigned char[frame_size.Width * frame_size.Height * 4];
int dstRowPitch = frame_size.Width * 4;
for (int h = 0; h < frame_size.Height; h++) {
memcpy_s(buffer + h * dstRowPitch, dstRowPitch,
(BYTE *)map_result.pData + h * map_result.RowPitch,
min(map_result.RowPitch, dstRowPitch));
}
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = frame_size.Width;
bi.biHeight = frame_size.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 + frame_size.Width * frame_size.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, frame_size.Width * frame_size.Height * 4, fp);
fflush(fp);
fclose(fp);
}
#endif
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
}
}
if (is_new_size) {
capture_framepool_.Recreate(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, capture_frame_size_);
}
}
void wgc_session_impl::on_closed(
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &,
winrt::Windows::Foundation::IInspectable const &) {
OutputDebugStringW(L"wgc_session_impl::on_closed");
}
int wgc_session_impl::initialize() {
if (is_initialized_)
return AM_ERROR::AE_NO;
if (!(d3d11_direct_device_ = create_d3d11_device()))
return AM_ERROR::AE_D3D_CREATE_DEVICE_FAILED;
try {
if (target_.is_window)
capture_item_ = create_capture_item(target_.hwnd);
else
capture_item_ = create_capture_item(target_.hmonitor);
// Set up
auto d3d11_device = get_dxgi_interface<ID3D11Device>(d3d11_direct_device_);
d3d11_device->GetImmediateContext(d3d11_device_context_.put());
} catch (winrt::hresult_error) {
return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
} catch (...) {
return AM_ERROR::AE_WGC_CREATE_CAPTURER_FAILED;
}
is_initialized_ = true;
return AM_ERROR::AE_NO;
}
void wgc_session_impl::cleanup() {
std::lock_guard locker(lock_);
auto expected = false;
if (cleaned_.compare_exchange_strong(expected, true)) {
capture_close_trigger_.revoke();
capture_framepool_trigger_.revoke();
if (capture_framepool_)
capture_framepool_.Close();
if (capture_session_)
capture_session_.Close();
capture_framepool_ = nullptr;
capture_session_ = nullptr;
capture_item_ = nullptr;
is_initialized_ = false;
}
}
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
LPARAM l_param) {
return DefWindowProc(window, message, w_param, l_param);
}
void wgc_session_impl::message_func() {
const std::wstring kClassName = L"am_fake_window";
WNDCLASS wc = {};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProc;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
wc.lpszClassName = kClassName.c_str();
if (!::RegisterClassW(&wc))
return;
hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW, 0,
0, 0, 0, nullptr, nullptr, nullptr, nullptr);
MSG msg;
while (is_running_) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (!is_running_)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
::CloseWindow(hwnd_);
::DestroyWindow(hwnd_);
}
} // namespace am

View File

@@ -0,0 +1,104 @@
#pragma once
#include <mutex>
#include <thread>
namespace am {
class wgc_session_impl : public wgc_session {
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
IDirect3DDxgiInterfaceAccess : ::IUnknown {
virtual HRESULT __stdcall GetInterface(GUID const &id, void **object) = 0;
};
struct {
union {
HWND hwnd;
HMONITOR hmonitor;
};
bool is_window;
} target_{0};
public:
wgc_session_impl();
~wgc_session_impl() override;
public:
void release() override;
int initialize(HWND hwnd) override;
int initialize(HMONITOR hmonitor) override;
void register_observer(wgc_session_observer *observer) override;
int start() override;
int stop() override;
int pause() override;
int resume() override;
private:
auto create_d3d11_device();
auto create_capture_item(HWND hwnd);
auto create_capture_item(HMONITOR hmonitor);
template <typename T>
auto
get_dxgi_interface(winrt::Windows::Foundation::IInspectable const &object);
HRESULT create_mapped_texture(winrt::com_ptr<ID3D11Texture2D> src_texture,
unsigned int width = 0,
unsigned int height = 0);
void
on_frame(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const
&sender,
winrt::Windows::Foundation::IInspectable const &args);
void on_closed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &,
winrt::Windows::Foundation::IInspectable const &);
int initialize();
void cleanup();
void message_func();
private:
std::mutex lock_;
bool is_initialized_ = false;
bool is_running_ = false;
bool is_paused_ = false;
wgc_session_observer *observer_ = nullptr;
// wgc
winrt::Windows::Graphics::Capture::GraphicsCaptureItem capture_item_{nullptr};
winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session_{
nullptr};
winrt::Windows::Graphics::SizeInt32 capture_frame_size_;
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice
d3d11_direct_device_{nullptr};
winrt::com_ptr<ID3D11DeviceContext> d3d11_device_context_{nullptr};
winrt::com_ptr<ID3D11Texture2D> d3d11_texture_mapped_{nullptr};
std::atomic<bool> cleaned_ = false;
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool
capture_framepool_{nullptr};
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
FrameArrived_revoker capture_framepool_trigger_;
winrt::Windows::Graphics::Capture::GraphicsCaptureItem::Closed_revoker
capture_close_trigger_;
// message loop
std::thread loop_;
HWND hwnd_ = nullptr;
};
template <typename T>
inline auto wgc_session_impl::get_dxgi_interface(
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;
}
} // namespace am

View File

@@ -7,12 +7,13 @@ set_languages("c++17")
add_rules("mode.release", "mode.debug")
add_requires("spdlog 1.11.0", "ffmpeg", {system = false})
add_requires("spdlog 1.11.0", "ffmpeg 5.1.2", {system = false})
add_defines("UNICODE")
if is_os("windows") then
add_ldflags("/SUBSYSTEM:CONSOLE")
add_links("Shell32")
add_requires("vcpkg::sdl2 2.26.4")
-- add_ldflags("/SUBSYSTEM:CONSOLE")
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32")
-- add_requires("vcpkg::sdl2")
elseif is_os("linux") then
add_links("pthread")
set_config("cxxflags", "-fPIC")
@@ -32,8 +33,8 @@ target("remote_desk")
set_kind("binary")
add_deps("projectx")
add_packages("log")
add_packages("vcpkg::sdl2", "ffmpeg")
add_packages("ffmpeg")
add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil")
add_files("*.cpp")
add_includedirs("../../src/interface")
add_links("SDL2main", "SDL2-static")
-- add_links("SDL2main", "SDL2-static")