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