mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 20:25:34 +08:00
Add wgc demo
This commit is contained in:
85
demo/App.cpp
Normal file
85
demo/App.cpp
Normal 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();
|
||||||
|
}
|
||||||
24
demo/App.h
Normal file
24
demo/App.h
Normal 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};
|
||||||
|
};
|
||||||
219
demo/SimpleCapture.cpp
Normal file
219
demo/SimpleCapture.cpp
Normal 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());
|
||||||
|
}
|
||||||
49
demo/SimpleCapture.h
Normal file
49
demo/SimpleCapture.h
Normal 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;
|
||||||
|
};
|
||||||
50
demo/Win32MonitorEnumeration.h
Normal file
50
demo/Win32MonitorEnumeration.h
Normal 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;
|
||||||
|
}
|
||||||
101
demo/Win32WindowEnumeration.h
Normal file
101
demo/Win32WindowEnumeration.h
Normal 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;
|
||||||
|
}
|
||||||
28
demo/capture.interop.h
Normal file
28
demo/capture.interop.h
Normal 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;
|
||||||
|
}
|
||||||
69
demo/composition.interop.h
Normal file
69
demo/composition.interop.h
Normal 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;
|
||||||
|
}
|
||||||
132
demo/d3dHelpers.h
Normal file
132
demo/d3dHelpers.h
Normal 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);
|
||||||
|
}
|
||||||
41
demo/direct3d11.interop.h
Normal file
41
demo/direct3d11.interop.h
Normal 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;
|
||||||
|
}
|
||||||
159
demo/main.cpp
Normal file
159
demo/main.cpp
Normal 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;
|
||||||
|
}
|
||||||
1
demo/pch.cpp
Normal file
1
demo/pch.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "pch.h"
|
||||||
34
demo/pch.h
Normal file
34
demo/pch.h
Normal 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"
|
||||||
19
dllmain.cpp
Normal file
19
dllmain.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
218
error_define.h
Normal file
218
error_define.h
Normal 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
|
||||||
17
export.cpp
Normal file
17
export.cpp
Normal 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(); }
|
||||||
50
export.h
Normal file
50
export.h
Normal 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();
|
||||||
21
head.h
Normal file
21
head.h
Normal 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
|
||||||
22
headers_ffmpeg.h
Normal file
22
headers_ffmpeg.h
Normal 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>
|
||||||
|
}
|
||||||
60
log_helper.cpp
Normal file
60
log_helper.cpp
Normal 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);
|
||||||
|
}
|
||||||
63
log_helper.h
Normal file
63
log_helper.h
Normal 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
|
||||||
17
main.cpp
Normal file
17
main.cpp
Normal 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;
|
||||||
|
}
|
||||||
48
pch.h
Normal file
48
pch.h
Normal 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
|
||||||
23
record_desktop.cpp
Normal file
23
record_desktop.cpp
Normal 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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
80
record_desktop.h
Normal file
80
record_desktop.h
Normal 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
|
||||||
45
record_desktop_define.h
Normal file
45
record_desktop_define.h
Normal 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
|
||||||
144
record_desktop_wgc.cpp
Normal file
144
record_desktop_wgc.cpp
Normal 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
|
||||||
65
record_desktop_wgc.h
Normal file
65
record_desktop_wgc.h
Normal 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
|
||||||
39
system_error.cpp
Normal file
39
system_error.cpp
Normal 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
|
||||||
12
system_error.h
Normal file
12
system_error.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace am {
|
||||||
|
|
||||||
|
class system_error {
|
||||||
|
public:
|
||||||
|
static const std::string& error2str(unsigned long error);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
66
utils_string.cpp
Normal file
66
utils_string.cpp
Normal 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
|
||||||
23
utils_string.h
Normal file
23
utils_string.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
425
wgc_session_impl.cpp
Normal file
425
wgc_session_impl.cpp
Normal 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
|
||||||
104
wgc_session_impl.h
Normal file
104
wgc_session_impl.h
Normal 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
|
||||||
13
xmake.lua
13
xmake.lua
@@ -7,12 +7,13 @@ set_languages("c++17")
|
|||||||
|
|
||||||
add_rules("mode.release", "mode.debug")
|
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
|
if is_os("windows") then
|
||||||
add_ldflags("/SUBSYSTEM:CONSOLE")
|
-- add_ldflags("/SUBSYSTEM:CONSOLE")
|
||||||
add_links("Shell32")
|
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32")
|
||||||
add_requires("vcpkg::sdl2 2.26.4")
|
-- add_requires("vcpkg::sdl2")
|
||||||
elseif is_os("linux") then
|
elseif is_os("linux") then
|
||||||
add_links("pthread")
|
add_links("pthread")
|
||||||
set_config("cxxflags", "-fPIC")
|
set_config("cxxflags", "-fPIC")
|
||||||
@@ -32,8 +33,8 @@ target("remote_desk")
|
|||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_deps("projectx")
|
add_deps("projectx")
|
||||||
add_packages("log")
|
add_packages("log")
|
||||||
add_packages("vcpkg::sdl2", "ffmpeg")
|
add_packages("ffmpeg")
|
||||||
add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil")
|
add_links("avfilter", "avdevice", "avformat", "avcodec", "swscale", "swresample", "avutil")
|
||||||
add_files("*.cpp")
|
add_files("*.cpp")
|
||||||
add_includedirs("../../src/interface")
|
add_includedirs("../../src/interface")
|
||||||
add_links("SDL2main", "SDL2-static")
|
-- add_links("SDL2main", "SDL2-static")
|
||||||
|
|||||||
Reference in New Issue
Block a user