添加屏幕捕获功能
parent
d3353c796e
commit
6798243ceb
|
@ -31,3 +31,7 @@ client/qt_gl_/yuvgl/yuvgl.pro.user.3ffb486
|
|||
client/qt_gl_/yuvgl/yuvgl.pro.user.4.10-pre1
|
||||
client/qt_gl_/yuvgl/yuvgl.pro.user.bccf4b5
|
||||
client/qt_gl_/yuvgl/yuvgl.pro.user.ed68183.4.8-pre1
|
||||
client/qt_gl_/yuvgl/debug/
|
||||
client/qt_gl_/build-yuvgl-Desktop_Qt_5_14_0_MSVC2017_64bit-Debug/
|
||||
client/qt_gl_/build-yuvgl-Desktop_Qt_5_14_0_MSVC2017_32bit-Debug/
|
||||
client/qt_gl_/build-yuvgl-Desktop_Qt_5_14_0_MinGW_64_bit-Debug/
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[requires]
|
||||
ffmpeg/4.2.1
|
||||
[imports]
|
||||
.,* -> ./third/msvc32 @ folder=True, ignore_case=True, excludes=*.html *.jpeg
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
#include <Unknwn.h>
|
||||
#include <strmif.h>
|
||||
|
||||
#pragma comment(lib, "strmiids.lib")
|
||||
|
||||
#ifndef __qedit_h__
|
||||
#define __qedit_h__
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct __declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85"))
|
||||
ISampleGrabberCB : IUnknown
|
||||
{
|
||||
//
|
||||
// Raw methods provided by interface
|
||||
//
|
||||
|
||||
virtual HRESULT __stdcall SampleCB(
|
||||
double SampleTime,
|
||||
struct IMediaSample * pSample) = 0;
|
||||
virtual HRESULT __stdcall BufferCB(
|
||||
double SampleTime,
|
||||
unsigned char * pBuffer,
|
||||
long BufferLen) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f"))
|
||||
ISampleGrabber : IUnknown
|
||||
{
|
||||
//
|
||||
// Raw methods provided by interface
|
||||
//
|
||||
|
||||
virtual HRESULT __stdcall SetOneShot(
|
||||
long OneShot) = 0;
|
||||
virtual HRESULT __stdcall SetMediaType(
|
||||
struct _AMMediaType * pType) = 0;
|
||||
virtual HRESULT __stdcall GetConnectedMediaType(
|
||||
struct _AMMediaType * pType) = 0;
|
||||
virtual HRESULT __stdcall SetBufferSamples(
|
||||
long BufferThem) = 0;
|
||||
virtual HRESULT __stdcall GetCurrentBuffer(
|
||||
/*[in,out]*/ long * pBufferSize,
|
||||
/*[out]*/ long * pBuffer) = 0;
|
||||
virtual HRESULT __stdcall GetCurrentSample(
|
||||
/*[out,retval]*/ struct IMediaSample * * ppSample) = 0;
|
||||
virtual HRESULT __stdcall SetCallback(
|
||||
struct ISampleGrabberCB * pCallback,
|
||||
long WhichMethodToCallback) = 0;
|
||||
};
|
||||
|
||||
|
||||
static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce,{ 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
|
||||
static const IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994,{ 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } };
|
||||
static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3,{ 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
||||
static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3,{ 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -66,6 +66,7 @@ extern void RTMP_TLS_Init();
|
|||
extern TLS_CTX RTMP_TLS_ctx;
|
||||
|
||||
#include <zlib.h>
|
||||
#include "strncasecmp.h"
|
||||
|
||||
#endif /* CRYPTO */
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "rtmp_sys.h"
|
||||
#include "log.h"
|
||||
#include "strncasecmp.h"
|
||||
|
||||
int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
|
||||
AVal *playpath, AVal *app)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "rtmp_sys.h"
|
||||
#include "log.h"
|
||||
#include "strncasecmp.h"
|
||||
|
||||
#ifdef CRYPTO
|
||||
#ifdef USE_POLARSSL
|
||||
|
@ -1165,7 +1166,7 @@ RTMP_ToggleStream(RTMP *r)
|
|||
return res;
|
||||
|
||||
r->m_pausing = 1;
|
||||
sleep(1);
|
||||
msleep(1000);
|
||||
}
|
||||
res = RTMP_SendPause(r, FALSE, r->m_pauseStamp);
|
||||
r->m_pausing = 3;
|
||||
|
@ -1524,10 +1525,10 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *packet)
|
|||
return bHasMediaPacket;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
extern FILE *netstackdump;
|
||||
extern FILE *netstackdump_read;
|
||||
#endif
|
||||
//#ifdef _DEBUG
|
||||
//extern FILE *netstackdump;
|
||||
//extern FILE *netstackdump_read;
|
||||
//#endif
|
||||
|
||||
// @remark debug info by http://github.com/ossrs/srs
|
||||
unsigned long _srs_rbytes = 0;
|
||||
|
@ -1620,7 +1621,7 @@ ReadN(RTMP *r, char *buffer, int n)
|
|||
}
|
||||
/*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
|
||||
#ifdef _DEBUG
|
||||
fwrite(ptr, 1, nBytes, netstackdump_read);
|
||||
// fwrite(ptr, 1, nBytes, netstackdump_read);
|
||||
#endif
|
||||
|
||||
if (nBytes == 0)
|
||||
|
@ -4469,7 +4470,7 @@ RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len)
|
|||
int rc;
|
||||
|
||||
#ifdef _DEBUG
|
||||
fwrite(buf, 1, len, netstackdump);
|
||||
// fwrite(buf, 1, len, netstackdump);
|
||||
#endif
|
||||
|
||||
#if defined(CRYPTO) && !defined(NO_SSL)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef STRNCASECMP_H
|
||||
#define STRNCASECMP_H
|
||||
|
||||
|
||||
#ifndef WINSHIT_INCLUDED
|
||||
#define WINSHIT_INCLUDED
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp(x,y,z) _strnicmp(x,y,z)
|
||||
|
||||
#endif /* Def WIN32 or Def WIN64 */
|
||||
|
||||
#endif /* Ndef WINSHIT_INCLUDED */
|
||||
|
||||
|
||||
#endif // STRNCASECMP_H
|
File diff suppressed because it is too large
Load Diff
|
@ -7,25 +7,27 @@
|
|||
#include <qlibrary.h>
|
||||
#include <qsysinfo.h>
|
||||
#include <qt_windows.h>
|
||||
#include "media/screen_capture.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <Tlhelp32.h>
|
||||
#include "winuser.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int RegiesterOwnType(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ScreenCapture g;
|
||||
g.EnumScreen();
|
||||
|
||||
QssEventFilter filter;
|
||||
QApplication app(argc, argv);
|
||||
|
||||
MainWindow main;
|
||||
main.setWindowTitle("视频采集rtmp推流工具");
|
||||
main.setWindowTitle("직첵竟꿎桿묏야");
|
||||
main.setFixedSize(1920,1080);
|
||||
main.show();
|
||||
return app.exec();
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>打开摄像头</string>
|
||||
<string>???????</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -99,7 +99,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>打开麦克风</string>
|
||||
<string>??????</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -116,7 +116,7 @@
|
|||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>rtmp推流地址</string>
|
||||
<string>rtmp???????</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -142,7 +142,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>推流</string>
|
||||
<string>????</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -15,7 +15,7 @@ void AACAudioCoder::OnAudioData(const void *frameaddress, uint32_t framelen)
|
|||
AACAudioCoder::AACAudioCoder(unsigned int smprate, unsigned int channel) {
|
||||
AVCodecID codec_id = AV_CODEC_ID_AAC;
|
||||
|
||||
pCodec = avcodec_find_encoder_by_name("libfdk_aac");
|
||||
pCodec = (AVCodec *)avcodec_find_encoder_by_name("libfdk_aac");
|
||||
if (!pCodec) {
|
||||
printf("Codec not found\n");
|
||||
this->mStatus = FAIL;
|
||||
|
|
|
@ -0,0 +1,683 @@
|
|||
/*****************************************************************************
|
||||
* DXGICapture.cpp
|
||||
*
|
||||
* Copyright (C) 2020 Gokhan Erdogdu <gokhan_erdogdu - at - yahoo - dot - com>
|
||||
*
|
||||
* DXGICapture is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* DXGICapture is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include "DXGICapture.h"
|
||||
#include "DXGICaptureHelper.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#pragma comment(lib, "D3D11.lib")
|
||||
#pragma comment(lib, "d2d1.lib")
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
|
||||
#pragma comment(lib, "shcore.lib") // SetProcessDpiAwareness
|
||||
|
||||
// Driver types supported
|
||||
const D3D_DRIVER_TYPE g_DriverTypes[] =
|
||||
{
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
D3D_DRIVER_TYPE_WARP,
|
||||
D3D_DRIVER_TYPE_REFERENCE,
|
||||
};
|
||||
const UINT g_NumDriverTypes = ARRAYSIZE(g_DriverTypes);
|
||||
|
||||
// Feature levels supported
|
||||
const D3D_FEATURE_LEVEL g_FeatureLevels[] =
|
||||
{
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_1,
|
||||
D3D_FEATURE_LEVEL_10_0,
|
||||
D3D_FEATURE_LEVEL_9_1
|
||||
};
|
||||
const UINT g_NumFeatureLevels = ARRAYSIZE(g_FeatureLevels);
|
||||
|
||||
#define AUTOLOCK() ATL::CComCritSecLock<ATL::CComAutoCriticalSection> auto_lock((ATL::CComAutoCriticalSection&)(m_csLock))
|
||||
|
||||
//
|
||||
// class CDXGICapture
|
||||
//
|
||||
CDXGICapture::CDXGICapture()
|
||||
: m_csLock()
|
||||
, m_bInitialized(FALSE)
|
||||
, m_lD3DFeatureLevel(D3D_FEATURE_LEVEL_INVALID)
|
||||
{
|
||||
RtlZeroMemory(&m_rendererInfo, sizeof(m_rendererInfo));
|
||||
RtlZeroMemory(&m_mouseInfo, sizeof(m_mouseInfo));
|
||||
RtlZeroMemory(&m_tempMouseBuffer, sizeof(m_tempMouseBuffer));
|
||||
RtlZeroMemory(&m_desktopOutputDesc, sizeof(m_desktopOutputDesc));
|
||||
}
|
||||
|
||||
CDXGICapture::~CDXGICapture()
|
||||
{
|
||||
this->Terminate();
|
||||
}
|
||||
|
||||
HRESULT CDXGICapture::loadMonitorInfos(ID3D11Device *pDevice)
|
||||
{
|
||||
CHECK_POINTER(pDevice);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
CComPtr<ID3D11Device> ipDevice(pDevice);
|
||||
|
||||
// Get DXGI device
|
||||
CComPtr<IDXGIDevice> ipDxgiDevice;
|
||||
hr = ipDevice->QueryInterface(IID_PPV_ARGS(&ipDxgiDevice));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Get DXGI adapter
|
||||
CComPtr<IDXGIAdapter> ipDxgiAdapter;
|
||||
hr = ipDxgiDevice->GetParent(IID_PPV_ARGS(&ipDxgiAdapter));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
ipDxgiDevice = nullptr;
|
||||
|
||||
CComPtr<IDXGIOutput> ipDxgiOutput;
|
||||
for (UINT i = 0; SUCCEEDED(hr); ++i)
|
||||
{
|
||||
ipDxgiOutput = nullptr;
|
||||
hr = ipDxgiAdapter->EnumOutputs(i, &ipDxgiOutput);
|
||||
if ((nullptr != ipDxgiOutput) && (hr != DXGI_ERROR_NOT_FOUND))
|
||||
{
|
||||
DXGI_OUTPUT_DESC DesktopDesc;
|
||||
hr = ipDxgiOutput->GetDesc(&DesktopDesc);
|
||||
if (FAILED(hr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tagDublicatorMonitorInfo *pInfo;
|
||||
pInfo = new (std::nothrow) tagDublicatorMonitorInfo;
|
||||
if (nullptr == pInfo) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
hr = DXGICaptureHelper::ConvertDxgiOutputToMonitorInfo(&DesktopDesc, i, pInfo);
|
||||
if (FAILED(hr)) {
|
||||
delete pInfo;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_monitorInfos.push_back(pInfo);
|
||||
}
|
||||
}
|
||||
|
||||
ipDxgiOutput = nullptr;
|
||||
ipDxgiAdapter = nullptr;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void CDXGICapture::freeMonitorInfos()
|
||||
{
|
||||
size_t nCount = m_monitorInfos.size();
|
||||
if (nCount == 0) {
|
||||
return;
|
||||
}
|
||||
DublicatorMonitorInfoVec::iterator it = m_monitorInfos.begin();
|
||||
DublicatorMonitorInfoVec::iterator end = m_monitorInfos.end();
|
||||
for (size_t i = 0; (i < nCount) && (it != end); i++, it++) {
|
||||
tagDublicatorMonitorInfo *pInfo = *it;
|
||||
if (nullptr != pInfo) {
|
||||
delete pInfo;
|
||||
}
|
||||
}
|
||||
m_monitorInfos.clear();
|
||||
}
|
||||
|
||||
HRESULT CDXGICapture::createDeviceResource(
|
||||
const tagScreenCaptureFilterConfig *pConfig,
|
||||
const tagDublicatorMonitorInfo *pSelectedMonitorInfo
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
CComPtr<IDXGIOutputDuplication> ipDxgiOutputDuplication;
|
||||
CComPtr<ID3D11Texture2D> ipCopyTexture2D;
|
||||
CComPtr<ID2D1Device> ipD2D1Device;
|
||||
CComPtr<ID2D1DeviceContext> ipD2D1DeviceContext;
|
||||
CComPtr<ID2D1Factory> ipD2D1Factory;
|
||||
CComPtr<IWICImagingFactory> ipWICImageFactory;
|
||||
CComPtr<IWICBitmap> ipWICOutputBitmap;
|
||||
CComPtr<ID2D1RenderTarget> ipD2D1RenderTarget;
|
||||
DXGI_OUTPUT_DESC dgixOutputDesc;
|
||||
tagRendererInfo rendererInfo;
|
||||
|
||||
RtlZeroMemory(&dgixOutputDesc, sizeof(dgixOutputDesc));
|
||||
RtlZeroMemory(&rendererInfo, sizeof(rendererInfo));
|
||||
|
||||
// copy configuration to renderer info
|
||||
rendererInfo.MonitorIdx = pConfig->MonitorIdx;
|
||||
rendererInfo.ShowCursor = pConfig->ShowCursor;
|
||||
rendererInfo.RotationMode = pConfig->RotationMode;
|
||||
rendererInfo.SizeMode = pConfig->SizeMode;
|
||||
rendererInfo.OutputSize = pConfig->OutputSize;
|
||||
// default
|
||||
rendererInfo.ScaleX = 1.0f;
|
||||
rendererInfo.ScaleY = 1.0f;
|
||||
|
||||
do
|
||||
{
|
||||
// Get DXGI factory
|
||||
CComPtr<IDXGIDevice> ipDxgiDevice;
|
||||
hr = m_ipD3D11Device->QueryInterface(IID_PPV_ARGS(&ipDxgiDevice));
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
CComPtr<IDXGIAdapter> ipDxgiAdapter;
|
||||
hr = ipDxgiDevice->GetParent(IID_PPV_ARGS(&ipDxgiAdapter));
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
// Get output
|
||||
CComPtr<IDXGIOutput> ipDxgiOutput;
|
||||
hr = ipDxgiAdapter->EnumOutputs(rendererInfo.MonitorIdx, &ipDxgiOutput);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
// Get output description
|
||||
hr = ipDxgiOutput->GetDesc(&dgixOutputDesc);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
tagDublicatorMonitorInfo curMonInfo;
|
||||
hr = DXGICaptureHelper::ConvertDxgiOutputToMonitorInfo(&dgixOutputDesc, rendererInfo.MonitorIdx, &curMonInfo);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
if (!DXGICaptureHelper::IsEqualMonitorInfo(pSelectedMonitorInfo, &curMonInfo)) {
|
||||
hr = E_INVALIDARG; // Monitor settings have changed ???
|
||||
break;
|
||||
}
|
||||
|
||||
// QI for Output 1
|
||||
CComPtr<IDXGIOutput1> ipDxgiOutput1;
|
||||
hr = ipDxgiOutput->QueryInterface(IID_PPV_ARGS(&ipDxgiOutput1));
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
// Create desktop duplication
|
||||
hr = ipDxgiOutput1->DuplicateOutput(m_ipD3D11Device, &ipDxgiOutputDuplication);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
DXGI_OUTDUPL_DESC dxgiOutputDuplDesc;
|
||||
ipDxgiOutputDuplication->GetDesc(&dxgiOutputDuplDesc);
|
||||
|
||||
hr = DXGICaptureHelper::CalculateRendererInfo(&dxgiOutputDuplDesc, &rendererInfo);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
// Create CPU access texture
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
desc.Width = rendererInfo.SrcBounds.Width;
|
||||
desc.Height = rendererInfo.SrcBounds.Height;
|
||||
desc.Format = rendererInfo.SrcFormat;
|
||||
desc.ArraySize = 1;
|
||||
desc.BindFlags = 0;
|
||||
desc.MiscFlags = 0;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.MipLevels = 1;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
|
||||
hr = m_ipD3D11Device->CreateTexture2D(&desc, NULL, &ipCopyTexture2D);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
if (nullptr == ipCopyTexture2D)
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
#pragma region <For_2D_operations>
|
||||
|
||||
// Create D2D1 device
|
||||
UINT uiFlags = m_ipD3D11Device->GetCreationFlags();
|
||||
D2D1_CREATION_PROPERTIES d2d1Props = D2D1::CreationProperties
|
||||
(
|
||||
(uiFlags & D3D11_CREATE_DEVICE_SINGLETHREADED)
|
||||
? D2D1_THREADING_MODE_SINGLE_THREADED
|
||||
: D2D1_THREADING_MODE_MULTI_THREADED,
|
||||
D2D1_DEBUG_LEVEL_NONE,
|
||||
(uiFlags & D3D11_CREATE_DEVICE_SINGLETHREADED)
|
||||
? D2D1_DEVICE_CONTEXT_OPTIONS_NONE
|
||||
: D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS
|
||||
);
|
||||
hr = D2D1CreateDevice(ipDxgiDevice, d2d1Props, &ipD2D1Device);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
// Get D2D1 factory
|
||||
ipD2D1Device->GetFactory(&ipD2D1Factory);
|
||||
|
||||
if (nullptr == ipD2D1Factory)
|
||||
{
|
||||
hr = D2DERR_INVALID_CALL;
|
||||
break;
|
||||
}
|
||||
|
||||
//create WIC factory
|
||||
hr = CoCreateInstance(
|
||||
CLSID_WICImagingFactory,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IWICImagingFactory,
|
||||
reinterpret_cast<void **>(&ipWICImageFactory)
|
||||
);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
// create D2D1 target bitmap for render
|
||||
hr = ipWICImageFactory->CreateBitmap(
|
||||
(UINT)rendererInfo.OutputSize.Width,
|
||||
(UINT)rendererInfo.OutputSize.Height,
|
||||
GUID_WICPixelFormat32bppPBGRA,
|
||||
WICBitmapCacheOnDemand,
|
||||
&ipWICOutputBitmap);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
if (nullptr == ipWICOutputBitmap)
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
// create a D2D1 render target (for D2D1 drawing)
|
||||
D2D1_RENDER_TARGET_PROPERTIES d2d1RenderTargetProp = D2D1::RenderTargetProperties
|
||||
(
|
||||
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
|
||||
0.0f, // default dpi
|
||||
0.0f, // default dpi
|
||||
D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE
|
||||
);
|
||||
hr = ipD2D1Factory->CreateWicBitmapRenderTarget(
|
||||
ipWICOutputBitmap,
|
||||
d2d1RenderTargetProp,
|
||||
&ipD2D1RenderTarget
|
||||
);
|
||||
CHECK_HR_BREAK(hr);
|
||||
|
||||
#pragma endregion </For_2D_operations>
|
||||
|
||||
} while (false);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// copy output parameters
|
||||
memcpy_s((void*)&m_rendererInfo, sizeof(m_rendererInfo), (const void*)&rendererInfo, sizeof(m_rendererInfo));
|
||||
|
||||
// set parameters
|
||||
m_desktopOutputDesc = dgixOutputDesc;
|
||||
|
||||
m_ipDxgiOutputDuplication = ipDxgiOutputDuplication;
|
||||
m_ipCopyTexture2D = ipCopyTexture2D;
|
||||
|
||||
m_ipD2D1Device = ipD2D1Device;
|
||||
m_ipD2D1Factory = ipD2D1Factory;
|
||||
m_ipWICImageFactory = ipWICImageFactory;
|
||||
m_ipWICOutputBitmap = ipWICOutputBitmap;
|
||||
m_ipD2D1RenderTarget = ipD2D1RenderTarget;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void CDXGICapture::terminateDeviceResource()
|
||||
{
|
||||
m_ipDxgiOutputDuplication = nullptr;
|
||||
m_ipCopyTexture2D = nullptr;
|
||||
|
||||
m_ipD2D1Device = nullptr;
|
||||
m_ipD2D1Factory = nullptr;
|
||||
m_ipWICImageFactory = nullptr;
|
||||
m_ipWICOutputBitmap = nullptr;
|
||||
m_ipD2D1RenderTarget = nullptr;
|
||||
|
||||
// clear config parameters
|
||||
RtlZeroMemory(&m_rendererInfo, sizeof(m_rendererInfo));
|
||||
|
||||
// clear mouse information parameters
|
||||
if (m_mouseInfo.PtrShapeBuffer != nullptr) {
|
||||
delete[] m_mouseInfo.PtrShapeBuffer;
|
||||
m_mouseInfo.PtrShapeBuffer = nullptr;
|
||||
}
|
||||
RtlZeroMemory(&m_mouseInfo, sizeof(m_mouseInfo));
|
||||
|
||||
// clear temp temp buffer
|
||||
if (m_tempMouseBuffer.Buffer != nullptr) {
|
||||
delete[] m_tempMouseBuffer.Buffer;
|
||||
m_tempMouseBuffer.Buffer = nullptr;
|
||||
}
|
||||
RtlZeroMemory(&m_tempMouseBuffer, sizeof(m_tempMouseBuffer));
|
||||
|
||||
// clear desktop output desc
|
||||
RtlZeroMemory(&m_desktopOutputDesc, sizeof(m_desktopOutputDesc));
|
||||
}
|
||||
|
||||
HRESULT CDXGICapture::Initialize()
|
||||
{
|
||||
AUTOLOCK();
|
||||
if (m_bInitialized) {
|
||||
return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); // already initialized
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
D3D_FEATURE_LEVEL lFeatureLevel;
|
||||
CComPtr<ID3D11Device> ipDevice;
|
||||
CComPtr<ID3D11DeviceContext> ipDeviceContext;
|
||||
|
||||
// required for monitor dpi problem (???)
|
||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
|
||||
// Create device
|
||||
for (UINT i = 0; i < g_NumDriverTypes; ++i)
|
||||
{
|
||||
hr = D3D11CreateDevice(
|
||||
nullptr,
|
||||
g_DriverTypes[i],
|
||||
nullptr,
|
||||
/* D3D11_CREATE_DEVICE_BGRA_SUPPORT
|
||||
* This flag adds support for surfaces with a different
|
||||
* color channel ordering than the API default.
|
||||
* You need it for compatibility with Direct2D. */
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
g_FeatureLevels,
|
||||
g_NumFeatureLevels,
|
||||
D3D11_SDK_VERSION,
|
||||
&ipDevice,
|
||||
&lFeatureLevel,
|
||||
&ipDeviceContext);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Device creation success, no need to loop anymore
|
||||
break;
|
||||
}
|
||||
|
||||
ipDevice = nullptr;
|
||||
ipDeviceContext = nullptr;
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (nullptr == ipDevice) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// load all monitor informations
|
||||
hr = loadMonitorInfos(ipDevice);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// set common fields
|
||||
m_lD3DFeatureLevel = lFeatureLevel;
|
||||
m_ipD3D11Device = ipDevice;
|
||||
m_ipD3D11DeviceContext = ipDeviceContext;
|
||||
|
||||
m_bInitialized = TRUE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CDXGICapture::Terminate()
|
||||
{
|
||||
AUTOLOCK();
|
||||
if (!m_bInitialized) {
|
||||
return S_FALSE; // already terminated
|
||||
}
|
||||
|
||||
this->terminateDeviceResource();
|
||||
|
||||
m_ipD3D11Device = nullptr;
|
||||
m_ipD3D11DeviceContext = nullptr;
|
||||
m_lD3DFeatureLevel = D3D_FEATURE_LEVEL_INVALID;
|
||||
|
||||
freeMonitorInfos();
|
||||
|
||||
m_bInitialized = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CDXGICapture::SetConfig(const tagScreenCaptureFilterConfig *pConfig)
|
||||
{
|
||||
AUTOLOCK();
|
||||
if (!m_bInitialized) {
|
||||
return D2DERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nullptr == pConfig) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
// terminate old resources
|
||||
this->terminateDeviceResource();
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
const tagDublicatorMonitorInfo *pSelectedMonitorInfo = nullptr;
|
||||
|
||||
pSelectedMonitorInfo = this->FindDublicatorMonitorInfo(pConfig->MonitorIdx);
|
||||
if (nullptr == pSelectedMonitorInfo) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
hr = this->createDeviceResource(pConfig, pSelectedMonitorInfo);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CDXGICapture::SetConfig(const tagScreenCaptureFilterConfig &config)
|
||||
{
|
||||
return this->SetConfig(&config);
|
||||
}
|
||||
|
||||
BOOL CDXGICapture::IsInitialized() const
|
||||
{
|
||||
AUTOLOCK();
|
||||
return m_bInitialized;
|
||||
}
|
||||
|
||||
D3D_FEATURE_LEVEL CDXGICapture::GetD3DFeatureLevel() const
|
||||
{
|
||||
AUTOLOCK();
|
||||
return m_lD3DFeatureLevel;
|
||||
}
|
||||
|
||||
int CDXGICapture::GetDublicatorMonitorInfoCount() const
|
||||
{
|
||||
AUTOLOCK();
|
||||
return (int)m_monitorInfos.size();
|
||||
}
|
||||
|
||||
const tagDublicatorMonitorInfo* CDXGICapture::GetDublicatorMonitorInfo(int index) const
|
||||
{
|
||||
AUTOLOCK();
|
||||
|
||||
size_t nCount = m_monitorInfos.size();
|
||||
if ((index < 0) || (index >= (int)nCount)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_monitorInfos[index];
|
||||
} // GetDublicatorMonitorInfo
|
||||
|
||||
const tagDublicatorMonitorInfo* CDXGICapture::FindDublicatorMonitorInfo(int monitorIdx) const
|
||||
{
|
||||
AUTOLOCK();
|
||||
|
||||
size_t nCount = m_monitorInfos.size();
|
||||
if (nCount == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
DublicatorMonitorInfoVec::const_iterator it = m_monitorInfos.begin();
|
||||
DublicatorMonitorInfoVec::const_iterator end = m_monitorInfos.end();
|
||||
for (size_t i = 0; (i < nCount) && (it != end); i++, it++) {
|
||||
tagDublicatorMonitorInfo *pInfo = *it;
|
||||
if (monitorIdx == pInfo->Idx) {
|
||||
return pInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
} // FindDublicatorMonitorInfo
|
||||
|
||||
//
|
||||
// CaptureToFile
|
||||
//
|
||||
HRESULT CDXGICapture::CaptureToFile(_In_ LPCWSTR lpcwOutputFileName, _Out_opt_ BOOL *pRetIsTimeout /*= NULL*/, _Out_opt_ UINT *pRetRenderDuration /*= NULL*/)
|
||||
{
|
||||
AUTOLOCK();
|
||||
|
||||
if (nullptr != pRetIsTimeout) {
|
||||
*pRetIsTimeout = FALSE;
|
||||
}
|
||||
|
||||
if (nullptr != pRetRenderDuration) {
|
||||
*pRetRenderDuration = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (!m_bInitialized) {
|
||||
return D2DERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
CHECK_POINTER_EX(m_ipDxgiOutputDuplication, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(lpcwOutputFileName, E_INVALIDARG);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
hr = DXGICaptureHelper::IsRendererInfoValid(&m_rendererInfo);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// is valid?
|
||||
hr = DXGICaptureHelper::GetContainerFormatByFileName(lpcwOutputFileName);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
|
||||
CComPtr<IDXGIResource> ipDesktopResource;
|
||||
CComPtr<ID3D11Texture2D> ipAcquiredDesktopImage;
|
||||
CComPtr<ID2D1Bitmap> ipD2D1SourceBitmap;
|
||||
|
||||
std::chrono::system_clock::time_point startTick;
|
||||
if (nullptr != pRetRenderDuration) {
|
||||
startTick = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
// Get new frame
|
||||
hr = m_ipDxgiOutputDuplication->AcquireNextFrame(1000, &FrameInfo, &ipDesktopResource);
|
||||
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
|
||||
{
|
||||
if (nullptr != pRetIsTimeout) {
|
||||
*pRetIsTimeout = TRUE;
|
||||
}
|
||||
return S_FALSE;
|
||||
}
|
||||
else if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
// QI for ID3D11Texture2D
|
||||
hr = ipDesktopResource->QueryInterface(IID_PPV_ARGS(&ipAcquiredDesktopImage));
|
||||
ipDesktopResource = nullptr;
|
||||
CHECK_HR_RETURN(hr);
|
||||
|
||||
if (nullptr == ipAcquiredDesktopImage)
|
||||
{
|
||||
// release frame
|
||||
m_ipDxgiOutputDuplication->ReleaseFrame();
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
// Copy needed full part of desktop image
|
||||
m_ipD3D11DeviceContext->CopyResource(m_ipCopyTexture2D, ipAcquiredDesktopImage);
|
||||
|
||||
if (m_rendererInfo.ShowCursor) {
|
||||
hr = DXGICaptureHelper::GetMouse(m_ipDxgiOutputDuplication, &m_mouseInfo, &FrameInfo, (UINT)m_rendererInfo.MonitorIdx, m_desktopOutputDesc.DesktopCoordinates.left, m_desktopOutputDesc.DesktopCoordinates.top);
|
||||
if (SUCCEEDED(hr) && m_mouseInfo.Visible) {
|
||||
hr = DXGICaptureHelper::DrawMouse(&m_mouseInfo, &m_desktopOutputDesc, &m_tempMouseBuffer, m_ipCopyTexture2D);
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
// release frame
|
||||
m_ipDxgiOutputDuplication->ReleaseFrame();
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
// release frame
|
||||
hr = m_ipDxgiOutputDuplication->ReleaseFrame();
|
||||
CHECK_HR_RETURN(hr);
|
||||
|
||||
// create D2D1 source bitmap
|
||||
hr = DXGICaptureHelper::CreateBitmap(m_ipD2D1RenderTarget, m_ipCopyTexture2D, &ipD2D1SourceBitmap);
|
||||
CHECK_HR_RETURN(hr);
|
||||
|
||||
D2D1_RECT_F rcSource = D2D1::RectF(
|
||||
(FLOAT)m_rendererInfo.SrcBounds.X,
|
||||
(FLOAT)m_rendererInfo.SrcBounds.Y,
|
||||
(FLOAT)(m_rendererInfo.SrcBounds.X + m_rendererInfo.SrcBounds.Width),
|
||||
(FLOAT)(m_rendererInfo.SrcBounds.Y + m_rendererInfo.SrcBounds.Height));
|
||||
D2D1_RECT_F rcTarget = D2D1::RectF(
|
||||
(FLOAT)m_rendererInfo.DstBounds.X,
|
||||
(FLOAT)m_rendererInfo.DstBounds.Y,
|
||||
(FLOAT)(m_rendererInfo.DstBounds.X + m_rendererInfo.DstBounds.Width),
|
||||
(FLOAT)(m_rendererInfo.DstBounds.Y + m_rendererInfo.DstBounds.Height));
|
||||
D2D1_POINT_2F ptTransformCenter = D2D1::Point2F(m_rendererInfo.OutputSize.Width / 2.0f, m_rendererInfo.OutputSize.Height / 2.0f);
|
||||
|
||||
// Apply the rotation transform to the render target.
|
||||
D2D1::Matrix3x2F rotate = D2D1::Matrix3x2F::Rotation(
|
||||
m_rendererInfo.RotationDegrees,
|
||||
ptTransformCenter
|
||||
);
|
||||
|
||||
D2D1::Matrix3x2F scale = D2D1::Matrix3x2F::Scale(
|
||||
D2D1::SizeF(m_rendererInfo.ScaleX, m_rendererInfo.ScaleY),
|
||||
ptTransformCenter
|
||||
);
|
||||
|
||||
// Priority: first rotate, after scale...
|
||||
m_ipD2D1RenderTarget->SetTransform(rotate * scale);
|
||||
|
||||
m_ipD2D1RenderTarget->BeginDraw();
|
||||
// clear background color
|
||||
m_ipD2D1RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black, 1.0f));
|
||||
m_ipD2D1RenderTarget->DrawBitmap(ipD2D1SourceBitmap, rcTarget, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rcSource);
|
||||
// Reset transform
|
||||
//m_ipD2D1RenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
|
||||
// Logo draw sample
|
||||
//m_ipD2D1RenderTarget->DrawBitmap(ipBmpLogo, D2D1::RectF(0, 0, 2 * 200, 2 * 46));
|
||||
hr = m_ipD2D1RenderTarget->EndDraw();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// calculate render time without save
|
||||
if (nullptr != pRetRenderDuration) {
|
||||
*pRetRenderDuration = (UINT)((std::chrono::system_clock::now() - startTick).count() / 10000);
|
||||
}
|
||||
|
||||
hr = DXGICaptureHelper::SaveImageToFile(m_ipWICImageFactory, m_ipWICOutputBitmap, lpcwOutputFileName);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
} // CaptureToFile
|
||||
|
||||
#undef AUTOLOCK
|
|
@ -0,0 +1,92 @@
|
|||
/*****************************************************************************
|
||||
* DXGICapture.h
|
||||
*
|
||||
* Copyright (C) 2020 Gokhan Erdogdu <gokhan_erdogdu - at - yahoo - dot - com>
|
||||
*
|
||||
* DXGICapture is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* DXGICapture is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
******************************************************************************/
|
||||
#pragma once
|
||||
#ifndef __DXGICAPTURE_H__
|
||||
#define __DXGICAPTURE_H__
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
|
||||
#include <dxgi1_2.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <d2d1_1.h> // for ID2D1Effect
|
||||
#include <wincodec.h>
|
||||
|
||||
#include "DXGICaptureTypes.h"
|
||||
|
||||
#define D3D_FEATURE_LEVEL_INVALID ((D3D_FEATURE_LEVEL)0x0)
|
||||
|
||||
class CDXGICapture
|
||||
{
|
||||
private:
|
||||
ATL::CComAutoCriticalSection m_csLock;
|
||||
|
||||
BOOL m_bInitialized;
|
||||
DublicatorMonitorInfoVec m_monitorInfos;
|
||||
tagRendererInfo m_rendererInfo;
|
||||
|
||||
tagMouseInfo m_mouseInfo;
|
||||
tagFrameBufferInfo m_tempMouseBuffer;
|
||||
DXGI_OUTPUT_DESC m_desktopOutputDesc;
|
||||
|
||||
D3D_FEATURE_LEVEL m_lD3DFeatureLevel;
|
||||
CComPtr<ID3D11Device> m_ipD3D11Device;
|
||||
CComPtr<ID3D11DeviceContext> m_ipD3D11DeviceContext;
|
||||
|
||||
CComPtr<IDXGIOutputDuplication> m_ipDxgiOutputDuplication;
|
||||
CComPtr<ID3D11Texture2D> m_ipCopyTexture2D;
|
||||
|
||||
CComPtr<ID2D1Device> m_ipD2D1Device;
|
||||
CComPtr<ID2D1Factory> m_ipD2D1Factory;
|
||||
CComPtr<IWICImagingFactory> m_ipWICImageFactory;
|
||||
CComPtr<IWICBitmap> m_ipWICOutputBitmap;
|
||||
CComPtr<ID2D1RenderTarget> m_ipD2D1RenderTarget;
|
||||
|
||||
public:
|
||||
CDXGICapture();
|
||||
~CDXGICapture();
|
||||
|
||||
private:
|
||||
HRESULT loadMonitorInfos(ID3D11Device *pDevice);
|
||||
void freeMonitorInfos();
|
||||
|
||||
HRESULT createDeviceResource(
|
||||
const tagScreenCaptureFilterConfig *pConfig,
|
||||
const tagDublicatorMonitorInfo *pSelectedMonitorInfo);
|
||||
void terminateDeviceResource();
|
||||
|
||||
public:
|
||||
HRESULT Initialize();
|
||||
HRESULT Terminate();
|
||||
HRESULT SetConfig(const tagScreenCaptureFilterConfig *pConfig);
|
||||
HRESULT SetConfig(const tagScreenCaptureFilterConfig &config);
|
||||
|
||||
BOOL IsInitialized() const;
|
||||
D3D_FEATURE_LEVEL GetD3DFeatureLevel() const;
|
||||
|
||||
int GetDublicatorMonitorInfoCount() const;
|
||||
const tagDublicatorMonitorInfo* GetDublicatorMonitorInfo(int index) const;
|
||||
const tagDublicatorMonitorInfo* FindDublicatorMonitorInfo(int monitorIdx) const;
|
||||
|
||||
HRESULT CaptureToFile(_In_ LPCWSTR lpcwOutputFileName, _Out_opt_ BOOL *pRetIsTimeout = NULL, _Out_opt_ UINT *pRetRenderDuration = NULL);
|
||||
};
|
||||
|
||||
#endif // __DXGICAPTURE_H__
|
||||
|
||||
|
|
@ -0,0 +1,960 @@
|
|||
/*****************************************************************************
|
||||
* DXGICaptureHelper.h
|
||||
*
|
||||
* Copyright (C) 2020 Gokhan Erdogdu <gokhan_erdogdu - at - yahoo - dot - com>
|
||||
*
|
||||
* DXGICapture is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* DXGICapture is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
******************************************************************************/
|
||||
#pragma once
|
||||
#ifndef __DXGICAPTUREHELPER_H__
|
||||
#define __DXGICAPTUREHELPER_H__
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <Shlwapi.h>
|
||||
|
||||
#include <dxgi1_2.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include "DXGICaptureTypes.h"
|
||||
|
||||
#pragma comment (lib, "Shlwapi.lib")
|
||||
|
||||
//
|
||||
// class DXGICaptureHelper
|
||||
//
|
||||
class DXGICaptureHelper
|
||||
{
|
||||
public:
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
ConvertDxgiOutputToMonitorInfo(
|
||||
_In_ const DXGI_OUTPUT_DESC *pDxgiOutput,
|
||||
_In_ int monitorIdx,
|
||||
_Out_ tagDublicatorMonitorInfo *pOutVal
|
||||
)
|
||||
{
|
||||
CHECK_POINTER(pOutVal);
|
||||
// reset output parameter
|
||||
RtlZeroMemory(pOutVal, sizeof(tagDublicatorMonitorInfo));
|
||||
CHECK_POINTER_EX(pDxgiOutput, E_INVALIDARG);
|
||||
|
||||
switch (pDxgiOutput->Rotation)
|
||||
{
|
||||
case DXGI_MODE_ROTATION_UNSPECIFIED:
|
||||
case DXGI_MODE_ROTATION_IDENTITY:
|
||||
pOutVal->RotationDegrees = 0;
|
||||
break;
|
||||
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
pOutVal->RotationDegrees = 90;
|
||||
break;
|
||||
|
||||
case DXGI_MODE_ROTATION_ROTATE180:
|
||||
pOutVal->RotationDegrees = 180;
|
||||
break;
|
||||
|
||||
case DXGI_MODE_ROTATION_ROTATE270:
|
||||
pOutVal->RotationDegrees = 270;
|
||||
break;
|
||||
}
|
||||
|
||||
pOutVal->Idx = monitorIdx;
|
||||
pOutVal->Bounds.X = pDxgiOutput->DesktopCoordinates.left;
|
||||
pOutVal->Bounds.Y = pDxgiOutput->DesktopCoordinates.top;
|
||||
pOutVal->Bounds.Width = pDxgiOutput->DesktopCoordinates.right - pDxgiOutput->DesktopCoordinates.left;
|
||||
pOutVal->Bounds.Height = pDxgiOutput->DesktopCoordinates.bottom - pDxgiOutput->DesktopCoordinates.top;
|
||||
|
||||
wsprintfW(pOutVal->DisplayName, L"Display %d: %ldx%ld @ %ld,%ld"
|
||||
, monitorIdx + 1
|
||||
, pOutVal->Bounds.Width, pOutVal->Bounds.Height
|
||||
, pOutVal->Bounds.X, pOutVal->Bounds.Y);
|
||||
|
||||
return S_OK;
|
||||
} // ConvertDxgiOutputToMonitorInfo
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
BOOL
|
||||
IsEqualMonitorInfo(
|
||||
_In_ const tagDublicatorMonitorInfo *p1,
|
||||
_In_ const tagDublicatorMonitorInfo *p2
|
||||
)
|
||||
{
|
||||
if (nullptr == p1) {
|
||||
return (nullptr == p2);
|
||||
}
|
||||
if (nullptr == p2) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return memcmp((const void*)p1, (const void*)p2, sizeof(tagDublicatorMonitorInfo)) == 0;
|
||||
} // IsEqualMonitorInfo
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
IsRendererInfoValid(
|
||||
_In_ const tagRendererInfo *pRendererInfo
|
||||
)
|
||||
{
|
||||
CHECK_POINTER_EX(pRendererInfo, E_INVALIDARG);
|
||||
|
||||
if (pRendererInfo->SrcFormat != DXGI_FORMAT_B8G8R8A8_UNORM) {
|
||||
return D2DERR_UNSUPPORTED_PIXEL_FORMAT;
|
||||
}
|
||||
|
||||
if (pRendererInfo->SizeMode != tagFrameSizeMode_Normal) {
|
||||
if ((pRendererInfo->OutputSize.Width <= 0) || (pRendererInfo->OutputSize.Height <= 0)) {
|
||||
return D2DERR_BITMAP_BOUND_AS_TARGET;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pRendererInfo->DstBounds.Width <= 0) || (pRendererInfo->DstBounds.Height <= 0) ||
|
||||
(pRendererInfo->SrcBounds.Width <= 0) || (pRendererInfo->SrcBounds.Height <= 0))
|
||||
{
|
||||
return D2DERR_ORIGINAL_TARGET_NOT_BOUND;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
CalculateRendererInfo(
|
||||
_In_ const DXGI_OUTDUPL_DESC *pDxgiOutputDuplDesc,
|
||||
_Inout_ tagRendererInfo *pRendererInfo
|
||||
)
|
||||
{
|
||||
CHECK_POINTER_EX(pDxgiOutputDuplDesc, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(pRendererInfo, E_INVALIDARG);
|
||||
|
||||
pRendererInfo->SrcFormat = pDxgiOutputDuplDesc->ModeDesc.Format;
|
||||
// get rotate state
|
||||
switch (pDxgiOutputDuplDesc->Rotation)
|
||||
{
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
pRendererInfo->RotationDegrees = 90.0f;
|
||||
pRendererInfo->SrcBounds.X = 0;
|
||||
pRendererInfo->SrcBounds.Y = 0;
|
||||
pRendererInfo->SrcBounds.Width = pDxgiOutputDuplDesc->ModeDesc.Height;
|
||||
pRendererInfo->SrcBounds.Height = pDxgiOutputDuplDesc->ModeDesc.Width;
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_ROTATE180:
|
||||
pRendererInfo->RotationDegrees = 180.0;
|
||||
pRendererInfo->SrcBounds.X = 0;
|
||||
pRendererInfo->SrcBounds.Y = 0;
|
||||
pRendererInfo->SrcBounds.Width = pDxgiOutputDuplDesc->ModeDesc.Width;
|
||||
pRendererInfo->SrcBounds.Height = pDxgiOutputDuplDesc->ModeDesc.Height;
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_ROTATE270:
|
||||
pRendererInfo->RotationDegrees = 270.0f;
|
||||
pRendererInfo->SrcBounds.X = 0;
|
||||
pRendererInfo->SrcBounds.Y = 0;
|
||||
pRendererInfo->SrcBounds.Width = pDxgiOutputDuplDesc->ModeDesc.Height;
|
||||
pRendererInfo->SrcBounds.Height = pDxgiOutputDuplDesc->ModeDesc.Width;
|
||||
break;
|
||||
default: // OR DXGI_MODE_ROTATION_IDENTITY:
|
||||
pRendererInfo->RotationDegrees = 0.0f;
|
||||
pRendererInfo->SrcBounds.X = 0;
|
||||
pRendererInfo->SrcBounds.Y = 0;
|
||||
pRendererInfo->SrcBounds.Width = pDxgiOutputDuplDesc->ModeDesc.Width;
|
||||
pRendererInfo->SrcBounds.Height = pDxgiOutputDuplDesc->ModeDesc.Height;
|
||||
break;
|
||||
}
|
||||
|
||||
// force rotate
|
||||
switch (pRendererInfo->RotationMode)
|
||||
{
|
||||
case tagFrameRotationMode::tagFrameRotationMode_Identity:
|
||||
pRendererInfo->RotationDegrees = 0.0f;
|
||||
break;
|
||||
case tagFrameRotationMode::tagFrameRotationMode_90:
|
||||
pRendererInfo->RotationDegrees = 90.0f;
|
||||
break;
|
||||
case tagFrameRotationMode::tagFrameRotationMode_180:
|
||||
pRendererInfo->RotationDegrees = 180.0f;
|
||||
break;
|
||||
case tagFrameRotationMode::tagFrameRotationMode_270:
|
||||
pRendererInfo->RotationDegrees = 270.0f;
|
||||
break;
|
||||
default: // tagFrameRotationMode::tagFrameRotationMode_Auto
|
||||
break;
|
||||
}
|
||||
|
||||
if (pRendererInfo->SizeMode == tagFrameSizeMode_Zoom)
|
||||
{
|
||||
FLOAT fSrcAspect, fOutAspect, fScaleFactor;
|
||||
|
||||
// center for output
|
||||
pRendererInfo->DstBounds.Width = pRendererInfo->SrcBounds.Width;
|
||||
pRendererInfo->DstBounds.Height = pRendererInfo->SrcBounds.Height;
|
||||
pRendererInfo->DstBounds.X = (pRendererInfo->OutputSize.Width - pRendererInfo->SrcBounds.Width) >> 1;
|
||||
pRendererInfo->DstBounds.Y = (pRendererInfo->OutputSize.Height - pRendererInfo->SrcBounds.Height) >> 1;
|
||||
|
||||
fOutAspect = (FLOAT)pRendererInfo->OutputSize.Width / pRendererInfo->OutputSize.Height;
|
||||
|
||||
if ((pRendererInfo->RotationDegrees == 0.0f) || (pRendererInfo->RotationDegrees == 180.0f))
|
||||
{
|
||||
fSrcAspect = (FLOAT)pRendererInfo->SrcBounds.Width / pRendererInfo->SrcBounds.Height;
|
||||
|
||||
if (fSrcAspect > fOutAspect)
|
||||
{
|
||||
fScaleFactor = (FLOAT)pRendererInfo->OutputSize.Width / pRendererInfo->SrcBounds.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
fScaleFactor = (FLOAT)pRendererInfo->OutputSize.Height / pRendererInfo->SrcBounds.Height;
|
||||
}
|
||||
}
|
||||
else // 90 or 270 degree
|
||||
{
|
||||
fSrcAspect = (FLOAT)pRendererInfo->SrcBounds.Height / pRendererInfo->SrcBounds.Width;
|
||||
|
||||
if (fSrcAspect > fOutAspect)
|
||||
{
|
||||
fScaleFactor = (FLOAT)pRendererInfo->OutputSize.Width / pRendererInfo->SrcBounds.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
fScaleFactor = (FLOAT)pRendererInfo->OutputSize.Height / pRendererInfo->SrcBounds.Width;
|
||||
}
|
||||
}
|
||||
|
||||
pRendererInfo->ScaleX = fScaleFactor;
|
||||
pRendererInfo->ScaleY = fScaleFactor;
|
||||
}
|
||||
else if (pRendererInfo->SizeMode == tagFrameSizeMode_CenterImage)
|
||||
{
|
||||
// center for output
|
||||
pRendererInfo->DstBounds.Width = pRendererInfo->SrcBounds.Width;
|
||||
pRendererInfo->DstBounds.Height = pRendererInfo->SrcBounds.Height;
|
||||
pRendererInfo->DstBounds.X = (pRendererInfo->OutputSize.Width - pRendererInfo->SrcBounds.Width) >> 1;
|
||||
pRendererInfo->DstBounds.Y = (pRendererInfo->OutputSize.Height - pRendererInfo->SrcBounds.Height) >> 1;
|
||||
}
|
||||
else if (pRendererInfo->SizeMode == tagFrameSizeMode_AutoSize)
|
||||
{
|
||||
// set the destination bounds
|
||||
pRendererInfo->DstBounds.Width = pRendererInfo->SrcBounds.Width;
|
||||
pRendererInfo->DstBounds.Height = pRendererInfo->SrcBounds.Height;
|
||||
|
||||
if ((pRendererInfo->RotationDegrees == 0.0f) || (pRendererInfo->RotationDegrees == 180.0f))
|
||||
{
|
||||
// same as the source size
|
||||
pRendererInfo->OutputSize.Width = pRendererInfo->SrcBounds.Width;
|
||||
pRendererInfo->OutputSize.Height = pRendererInfo->SrcBounds.Height;
|
||||
}
|
||||
else // 90 or 270 degree
|
||||
{
|
||||
// same as the source size
|
||||
pRendererInfo->OutputSize.Width = pRendererInfo->SrcBounds.Height;
|
||||
pRendererInfo->OutputSize.Height = pRendererInfo->SrcBounds.Width;
|
||||
|
||||
// center for output
|
||||
pRendererInfo->DstBounds.X = (pRendererInfo->OutputSize.Width - pRendererInfo->SrcBounds.Width) >> 1;
|
||||
pRendererInfo->DstBounds.Y = (pRendererInfo->OutputSize.Height - pRendererInfo->SrcBounds.Height) >> 1;
|
||||
}
|
||||
}
|
||||
else if (pRendererInfo->SizeMode == tagFrameSizeMode_StretchImage)
|
||||
{
|
||||
// center for output
|
||||
pRendererInfo->DstBounds.Width = pRendererInfo->SrcBounds.Width;
|
||||
pRendererInfo->DstBounds.Height = pRendererInfo->SrcBounds.Height;
|
||||
pRendererInfo->DstBounds.X = (pRendererInfo->OutputSize.Width - pRendererInfo->SrcBounds.Width) >> 1;
|
||||
pRendererInfo->DstBounds.Y = (pRendererInfo->OutputSize.Height - pRendererInfo->SrcBounds.Height) >> 1;
|
||||
|
||||
if ((pRendererInfo->RotationDegrees == 0.0f) || (pRendererInfo->RotationDegrees == 180.0f))
|
||||
{
|
||||
pRendererInfo->ScaleX = (FLOAT)pRendererInfo->OutputSize.Width / pRendererInfo->DstBounds.Width;
|
||||
pRendererInfo->ScaleY = (FLOAT)pRendererInfo->OutputSize.Height / pRendererInfo->DstBounds.Height;
|
||||
}
|
||||
else // 90 or 270 degree
|
||||
{
|
||||
pRendererInfo->ScaleX = (FLOAT)pRendererInfo->OutputSize.Width / pRendererInfo->DstBounds.Height;
|
||||
pRendererInfo->ScaleY = (FLOAT)pRendererInfo->OutputSize.Height / pRendererInfo->DstBounds.Width;
|
||||
}
|
||||
}
|
||||
else // tagFrameSizeMode_Normal
|
||||
{
|
||||
pRendererInfo->DstBounds.Width = pRendererInfo->SrcBounds.Width;
|
||||
pRendererInfo->DstBounds.Height = pRendererInfo->SrcBounds.Height;
|
||||
|
||||
if (pRendererInfo->RotationDegrees == 90)
|
||||
{
|
||||
// set destination origin (bottom-left)
|
||||
pRendererInfo->DstBounds.X = (pRendererInfo->OutputSize.Width - pRendererInfo->OutputSize.Height) >> 1;
|
||||
pRendererInfo->DstBounds.Y = ((pRendererInfo->OutputSize.Width + pRendererInfo->OutputSize.Height) >> 1) - pRendererInfo->DstBounds.Height;
|
||||
}
|
||||
else if (pRendererInfo->RotationDegrees == 180.0f)
|
||||
{
|
||||
// set destination origin (bottom-right)
|
||||
pRendererInfo->DstBounds.X = pRendererInfo->OutputSize.Width - pRendererInfo->DstBounds.Width;
|
||||
pRendererInfo->DstBounds.Y = pRendererInfo->OutputSize.Height - pRendererInfo->DstBounds.Height;
|
||||
}
|
||||
else if (pRendererInfo->RotationDegrees == 270)
|
||||
{
|
||||
// set destination origin (top-right)
|
||||
pRendererInfo->DstBounds.Y = (pRendererInfo->OutputSize.Height - pRendererInfo->OutputSize.Width) >> 1;
|
||||
pRendererInfo->DstBounds.X = pRendererInfo->OutputSize.Width - pRendererInfo->DstBounds.Width - ((pRendererInfo->OutputSize.Width - pRendererInfo->OutputSize.Height) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
ResizeFrameBuffer(
|
||||
_Inout_ tagFrameBufferInfo *pBufferInfo,
|
||||
_In_ UINT uiNewSize
|
||||
)
|
||||
{
|
||||
CHECK_POINTER(pBufferInfo);
|
||||
|
||||
if (uiNewSize <= pBufferInfo->BufferSize)
|
||||
{
|
||||
return S_FALSE; // no change
|
||||
}
|
||||
|
||||
if (nullptr != pBufferInfo->Buffer) {
|
||||
delete[] pBufferInfo->Buffer;
|
||||
pBufferInfo->Buffer = nullptr;
|
||||
}
|
||||
|
||||
pBufferInfo->Buffer = new (std::nothrow) BYTE[uiNewSize];
|
||||
if (!(pBufferInfo->Buffer))
|
||||
{
|
||||
pBufferInfo->BufferSize = 0;
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
pBufferInfo->BufferSize = uiNewSize;
|
||||
|
||||
return S_OK;
|
||||
} // ResizeFrameBuffer
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
GetMouse(
|
||||
_In_ IDXGIOutputDuplication *pOutputDuplication,
|
||||
_Inout_ tagMouseInfo *PtrInfo,
|
||||
_In_ DXGI_OUTDUPL_FRAME_INFO *FrameInfo,
|
||||
UINT MonitorIdx,
|
||||
INT OffsetX,
|
||||
INT OffsetY
|
||||
)
|
||||
{
|
||||
CHECK_POINTER_EX(pOutputDuplication, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(PtrInfo, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(FrameInfo, E_INVALIDARG);
|
||||
|
||||
// A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
|
||||
if (FrameInfo->LastMouseUpdateTime.QuadPart == 0)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool UpdatePosition = true;
|
||||
|
||||
// Make sure we don't update pointer position wrongly
|
||||
// If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
|
||||
// was visible, if so, don't set it to invisible or update.
|
||||
if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != MonitorIdx))
|
||||
{
|
||||
UpdatePosition = false;
|
||||
}
|
||||
|
||||
// If two outputs both say they have a visible, only update if new update has newer timestamp
|
||||
if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != MonitorIdx) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart))
|
||||
{
|
||||
UpdatePosition = false;
|
||||
}
|
||||
|
||||
// Update position
|
||||
if (UpdatePosition)
|
||||
{
|
||||
PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x - OffsetX;
|
||||
PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y - OffsetY;
|
||||
PtrInfo->WhoUpdatedPositionLast = MonitorIdx;
|
||||
PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime;
|
||||
PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0;
|
||||
}
|
||||
|
||||
// No new shape
|
||||
if (FrameInfo->PointerShapeBufferSize == 0)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Old buffer too small
|
||||
if (FrameInfo->PointerShapeBufferSize > PtrInfo->ShapeBufferSize)
|
||||
{
|
||||
if (PtrInfo->PtrShapeBuffer != nullptr)
|
||||
{
|
||||
delete[] PtrInfo->PtrShapeBuffer;
|
||||
PtrInfo->PtrShapeBuffer = nullptr;
|
||||
}
|
||||
PtrInfo->PtrShapeBuffer = new (std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize];
|
||||
if (PtrInfo->PtrShapeBuffer == nullptr)
|
||||
{
|
||||
PtrInfo->ShapeBufferSize = 0;
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
// Update buffer size
|
||||
PtrInfo->ShapeBufferSize = FrameInfo->PointerShapeBufferSize;
|
||||
}
|
||||
|
||||
// Get shape
|
||||
UINT BufferSizeRequired;
|
||||
HRESULT hr = pOutputDuplication->GetFramePointerShape(
|
||||
FrameInfo->PointerShapeBufferSize,
|
||||
reinterpret_cast<VOID*>(PtrInfo->PtrShapeBuffer),
|
||||
&BufferSizeRequired,
|
||||
&(PtrInfo->ShapeInfo)
|
||||
);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
delete[] PtrInfo->PtrShapeBuffer;
|
||||
PtrInfo->PtrShapeBuffer = nullptr;
|
||||
PtrInfo->ShapeBufferSize = 0;
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
} // GetMouse
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
ProcessMouseMask(
|
||||
_In_ const tagMouseInfo *PtrInfo,
|
||||
_In_ const DXGI_OUTPUT_DESC *DesktopDesc,
|
||||
_Inout_ tagFrameBufferInfo *pBufferInfo
|
||||
)
|
||||
{
|
||||
CHECK_POINTER_EX(PtrInfo, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(DesktopDesc, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(pBufferInfo, E_INVALIDARG);
|
||||
|
||||
if (!PtrInfo->Visible) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
INT DesktopWidth = (INT)(DesktopDesc->DesktopCoordinates.right - DesktopDesc->DesktopCoordinates.left);
|
||||
INT DesktopHeight = (INT)(DesktopDesc->DesktopCoordinates.bottom - DesktopDesc->DesktopCoordinates.top);
|
||||
|
||||
pBufferInfo->Bounds.X = PtrInfo->Position.x;
|
||||
pBufferInfo->Bounds.Y = PtrInfo->Position.y;
|
||||
pBufferInfo->Bounds.Width = PtrInfo->ShapeInfo.Width;
|
||||
pBufferInfo->Bounds.Height = (PtrInfo->ShapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME)
|
||||
? (INT)(PtrInfo->ShapeInfo.Height / 2)
|
||||
: (INT)PtrInfo->ShapeInfo.Height;
|
||||
pBufferInfo->Pitch = pBufferInfo->Bounds.Width * 4;
|
||||
|
||||
switch (PtrInfo->ShapeInfo.Type)
|
||||
{
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
{
|
||||
// Resize mouseshape buffer (if necessary)
|
||||
hr = DXGICaptureHelper::ResizeFrameBuffer(pBufferInfo, PtrInfo->ShapeBufferSize);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// use current mouseshape buffer
|
||||
// Copy mouseshape buffer
|
||||
memcpy_s((void*)pBufferInfo->Buffer, pBufferInfo->BufferSize, (const void*)PtrInfo->PtrShapeBuffer, PtrInfo->ShapeBufferSize);
|
||||
break;
|
||||
}
|
||||
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
|
||||
{
|
||||
// Resize mouseshape buffer (if necessary)
|
||||
hr = DXGICaptureHelper::ResizeFrameBuffer(pBufferInfo, pBufferInfo->Bounds.Height * pBufferInfo->Pitch);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
UINT* InitBuffer32 = reinterpret_cast<UINT*>(pBufferInfo->Buffer);
|
||||
|
||||
for (INT Row = 0; Row < pBufferInfo->Bounds.Height; ++Row)
|
||||
{
|
||||
// Set mask
|
||||
BYTE Mask = 0x80;
|
||||
for (INT Col = 0; Col < pBufferInfo->Bounds.Width; ++Col)
|
||||
{
|
||||
BYTE XorMask = PtrInfo->PtrShapeBuffer[(Col / 8) + ((Row + (PtrInfo->ShapeInfo.Height / 2)) * (PtrInfo->ShapeInfo.Pitch))] & Mask;
|
||||
|
||||
// Set new pixel
|
||||
InitBuffer32[(Row * pBufferInfo->Bounds.Width) + Col] = (XorMask) ? 0xFFFFFFFF : 0x00000000;
|
||||
|
||||
// Adjust mask
|
||||
if (Mask == 0x01)
|
||||
{
|
||||
Mask = 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
Mask = Mask >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
|
||||
{
|
||||
// Resize mouseshape buffer (if necessary)
|
||||
hr = DXGICaptureHelper::ResizeFrameBuffer(pBufferInfo, pBufferInfo->Bounds.Height * pBufferInfo->Pitch);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
UINT* InitBuffer32 = reinterpret_cast<UINT*>(pBufferInfo->Buffer);
|
||||
UINT* ShapeBuffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer);
|
||||
|
||||
for (INT Row = 0; Row < pBufferInfo->Bounds.Height; ++Row)
|
||||
{
|
||||
for (INT Col = 0; Col < pBufferInfo->Bounds.Width; ++Col)
|
||||
{
|
||||
InitBuffer32[(Row * pBufferInfo->Bounds.Width) + Col] = ShapeBuffer32[Col + (Row * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))] | 0xFF000000;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return E_INVALIDARG;
|
||||
|
||||
}
|
||||
|
||||
UINT* InitBuffer32 = reinterpret_cast<UINT*>(pBufferInfo->Buffer);
|
||||
UINT width = (UINT)pBufferInfo->Bounds.Width;
|
||||
UINT height = (UINT)pBufferInfo->Bounds.Height;
|
||||
|
||||
switch (DesktopDesc->Rotation)
|
||||
{
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
{
|
||||
// Rotate -90 or +270
|
||||
for (UINT i = 0; i < width; i++)
|
||||
{
|
||||
for (UINT j = 0; j < height; j++)
|
||||
{
|
||||
UINT I = j;
|
||||
UINT J = width - 1 - i;
|
||||
while ((i*height + j) >(I*width + J))
|
||||
{
|
||||
UINT p = I*width + J;
|
||||
UINT tmp_i = p / height;
|
||||
UINT tmp_j = p % height;
|
||||
I = tmp_j;
|
||||
J = width - 1 - tmp_i;
|
||||
}
|
||||
std::swap(*(InitBuffer32 + (i*height + j)), *(InitBuffer32 + (I*width + J)));
|
||||
}
|
||||
}
|
||||
|
||||
// translate bounds
|
||||
std::swap(pBufferInfo->Bounds.Width, pBufferInfo->Bounds.Height);
|
||||
INT nX = pBufferInfo->Bounds.Y;
|
||||
INT nY = DesktopWidth - (INT)(pBufferInfo->Bounds.X + pBufferInfo->Bounds.Height);
|
||||
pBufferInfo->Bounds.X = nX;
|
||||
pBufferInfo->Bounds.Y = nY;
|
||||
pBufferInfo->Pitch = pBufferInfo->Bounds.Width * 4;
|
||||
} break;
|
||||
case DXGI_MODE_ROTATION_ROTATE180:
|
||||
{
|
||||
// Rotate -180 or +180
|
||||
if (height % 2 != 0)
|
||||
{
|
||||
//If N is odd reverse the middle row in the matrix
|
||||
UINT j = height >> 1;
|
||||
for (UINT i = 0; i < (width >> 1); i++)
|
||||
{
|
||||
std::swap(InitBuffer32[j * width + i], InitBuffer32[j * width + width - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
for (UINT j = 0; j < (height >> 1); j++)
|
||||
{
|
||||
for (UINT i = 0; i < width; i++)
|
||||
{
|
||||
std::swap(InitBuffer32[j * width + i], InitBuffer32[(height - j - 1) * width + width - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// translate position
|
||||
INT nX = DesktopWidth - (INT)(pBufferInfo->Bounds.X + pBufferInfo->Bounds.Width);
|
||||
INT nY = DesktopHeight - (INT)(pBufferInfo->Bounds.Y + pBufferInfo->Bounds.Height);
|
||||
pBufferInfo->Bounds.X = nX;
|
||||
pBufferInfo->Bounds.Y = nY;
|
||||
} break;
|
||||
case DXGI_MODE_ROTATION_ROTATE270:
|
||||
{
|
||||
// Rotate -270 or +90
|
||||
for (UINT i = 0; i < width; i++)
|
||||
{
|
||||
for (UINT j = 0; j < height; j++)
|
||||
{
|
||||
UINT I = height - 1 - j;
|
||||
UINT J = i;
|
||||
while ((i*height + j) >(I*width + J))
|
||||
{
|
||||
int p = I*width + J;
|
||||
int tmp_i = p / height;
|
||||
int tmp_j = p % height;
|
||||
I = height - 1 - tmp_j;
|
||||
J = tmp_i;
|
||||
}
|
||||
std::swap(*(InitBuffer32 + (i*height + j)), *(InitBuffer32 + (I*width + J)));
|
||||
}
|
||||
}
|
||||
|
||||
// translate bounds
|
||||
std::swap(pBufferInfo->Bounds.Width, pBufferInfo->Bounds.Height);
|
||||
INT nX = DesktopHeight - (pBufferInfo->Bounds.Y + pBufferInfo->Bounds.Width);
|
||||
INT nY = pBufferInfo->Bounds.X;
|
||||
pBufferInfo->Bounds.X = nX;
|
||||
pBufferInfo->Bounds.Y = nY;
|
||||
pBufferInfo->Pitch = pBufferInfo->Bounds.Width * 4;
|
||||
} break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
} // ProcessMouseMask
|
||||
|
||||
//
|
||||
// Draw mouse provided in buffer to backbuffer
|
||||
//
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
DrawMouse(
|
||||
_In_ tagMouseInfo *PtrInfo,
|
||||
_In_ const DXGI_OUTPUT_DESC *DesktopDesc,
|
||||
_Inout_ tagFrameBufferInfo *pTempMouseBuffer,
|
||||
_Inout_ ID3D11Texture2D *pSharedSurf
|
||||
)
|
||||
{
|
||||
CHECK_POINTER_EX(PtrInfo, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(DesktopDesc, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(pTempMouseBuffer, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(pSharedSurf, E_INVALIDARG);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
D3D11_TEXTURE2D_DESC FullDesc;
|
||||
pSharedSurf->GetDesc(&FullDesc);
|
||||
|
||||
INT SurfWidth = FullDesc.Width;
|
||||
INT SurfHeight = FullDesc.Height;
|
||||
INT SurfPitch = FullDesc.Width * 4;
|
||||
|
||||
hr = DXGICaptureHelper::ProcessMouseMask(PtrInfo, DesktopDesc, pTempMouseBuffer);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Buffer used if necessary (in case of monochrome or masked pointer)
|
||||
BYTE* InitBuffer = pTempMouseBuffer->Buffer;
|
||||
|
||||
// Clipping adjusted coordinates / dimensions
|
||||
INT PtrWidth = (INT)pTempMouseBuffer->Bounds.Width;
|
||||
INT PtrHeight = (INT)pTempMouseBuffer->Bounds.Height;
|
||||
|
||||
INT PtrLeft = (INT)pTempMouseBuffer->Bounds.X;
|
||||
INT PtrTop = (INT)pTempMouseBuffer->Bounds.Y;
|
||||
INT PtrPitch = (INT)pTempMouseBuffer->Pitch;
|
||||
|
||||
INT SrcLeft = 0;
|
||||
INT SrcTop = 0;
|
||||
INT SrcWidth = PtrWidth;
|
||||
INT SrcHeight = PtrHeight;
|
||||
|
||||
if (PtrLeft < 0)
|
||||
{
|
||||
// crop mouseshape left
|
||||
SrcLeft = -PtrLeft;
|
||||
// new mouse x position for drawing
|
||||
PtrLeft = 0;
|
||||
}
|
||||
else if (PtrLeft + PtrWidth > SurfWidth)
|
||||
{
|
||||
// crop mouseshape width
|
||||
SrcWidth = SurfWidth - PtrLeft;
|
||||
}
|
||||
|
||||
if (PtrTop < 0)
|
||||
{
|
||||
// crop mouseshape top
|
||||
SrcTop = -PtrTop;
|
||||
// new mouse y position for drawing
|
||||
PtrTop = 0;
|
||||
}
|
||||
else if (PtrTop + PtrHeight > SurfHeight)
|
||||
{
|
||||
// crop mouseshape height
|
||||
SrcHeight = SurfHeight - PtrTop;
|
||||
}
|
||||
|
||||
// QI for IDXGISurface
|
||||
CComPtr<IDXGISurface> ipCopySurface;
|
||||
hr = pSharedSurf->QueryInterface(__uuidof(IDXGISurface), (void **)&ipCopySurface);
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Map pixels
|
||||
DXGI_MAPPED_RECT MappedSurface;
|
||||
hr = ipCopySurface->Map(&MappedSurface, DXGI_MAP_READ | DXGI_MAP_WRITE);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// 0xAARRGGBB
|
||||
UINT* SrcBuffer32 = reinterpret_cast<UINT*>(InitBuffer);
|
||||
UINT* DstBuffer32 = reinterpret_cast<UINT*>(MappedSurface.pBits) + PtrTop * SurfWidth + PtrLeft;
|
||||
|
||||
// Alpha blending masks
|
||||
const UINT AMask = 0xFF000000;
|
||||
const UINT RBMask = 0x00FF00FF;
|
||||
const UINT GMask = 0x0000FF00;
|
||||
const UINT AGMask = AMask | GMask;
|
||||
const UINT OneAlpha = 0x01000000;
|
||||
UINT uiPixel1;
|
||||
UINT uiPixel2;
|
||||
UINT uiAlpha;
|
||||
UINT uiNAlpha;
|
||||
UINT uiRedBlue;
|
||||
UINT uiAlphaGreen;
|
||||
|
||||
for (INT Row = SrcTop; Row < SrcHeight; ++Row)
|
||||
{
|
||||
for (INT Col = SrcLeft; Col < SrcWidth; ++Col)
|
||||
{
|
||||
// Alpha blending
|
||||
uiPixel1 = DstBuffer32[((Row - SrcTop) * SurfWidth) + (Col - SrcLeft)];
|
||||
uiPixel2 = SrcBuffer32[(Row * PtrWidth) + Col];
|
||||
uiAlpha = (uiPixel2 & AMask) >> 24;
|
||||
uiNAlpha = 255 - uiAlpha;
|
||||
uiRedBlue = ((uiNAlpha * (uiPixel1 & RBMask)) + (uiAlpha * (uiPixel2 & RBMask))) >> 8;
|
||||
uiAlphaGreen = (uiNAlpha * ((uiPixel1 & AGMask) >> 8)) + (uiAlpha * (OneAlpha | ((uiPixel2 & GMask) >> 8)));
|
||||
|
||||
DstBuffer32[((Row - SrcTop) * SurfWidth) + (Col - SrcLeft)] = ((uiRedBlue & RBMask) | (uiAlphaGreen & AGMask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Done with resource
|
||||
hr = ipCopySurface->Unmap();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
} // DrawMouse
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
CreateBitmap(
|
||||
_In_ ID2D1RenderTarget *pRenderTarget,
|
||||
_In_ ID3D11Texture2D *pSourceTexture,
|
||||
_Outptr_ ID2D1Bitmap **ppOutBitmap
|
||||
)
|
||||
{
|
||||
CHECK_POINTER(ppOutBitmap);
|
||||
*ppOutBitmap = nullptr;
|
||||
CHECK_POINTER_EX(pRenderTarget, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(pSourceTexture, E_INVALIDARG);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
CComPtr<ID3D11Texture2D> ipSourceTexture(pSourceTexture);
|
||||
CComPtr<IDXGISurface> ipCopySurface;
|
||||
CComPtr<ID2D1Bitmap> ipD2D1SourceBitmap;
|
||||
|
||||
// QI for IDXGISurface
|
||||
hr = ipSourceTexture->QueryInterface(__uuidof(IDXGISurface), (void **)&ipCopySurface);
|
||||
CHECK_HR_RETURN(hr);
|
||||
|
||||
// Map pixels
|
||||
DXGI_MAPPED_RECT MappedSurface;
|
||||
hr = ipCopySurface->Map(&MappedSurface, DXGI_MAP_READ);
|
||||
CHECK_HR_RETURN(hr);
|
||||
|
||||
D3D11_TEXTURE2D_DESC destImageDesc;
|
||||
ipSourceTexture->GetDesc(&destImageDesc);
|
||||
|
||||
hr = pRenderTarget->CreateBitmap(
|
||||
D2D1::SizeU(destImageDesc.Width, destImageDesc.Height),
|
||||
(const void*)MappedSurface.pBits,
|
||||
MappedSurface.Pitch,
|
||||
D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
|
||||
&ipD2D1SourceBitmap);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Done with resource
|
||||
hr = ipCopySurface->Unmap();
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Done with resource
|
||||
hr = ipCopySurface->Unmap();
|
||||
CHECK_HR_RETURN(hr);
|
||||
|
||||
// set return value
|
||||
*ppOutBitmap = ipD2D1SourceBitmap.Detach();
|
||||
|
||||
return S_OK;
|
||||
} // CreateBitmap
|
||||
|
||||
static
|
||||
inline
|
||||
COM_DECLSPEC_NOTHROW
|
||||
HRESULT
|
||||
GetContainerFormatByFileName(
|
||||
_In_ LPCWSTR lpcwFileName,
|
||||
_Out_opt_ GUID *pRetVal = NULL
|
||||
)
|
||||
{
|
||||
RESET_POINTER_EX(pRetVal, GUID_NULL);
|
||||
CHECK_POINTER_EX(lpcwFileName, E_INVALIDARG);
|
||||
|
||||
if (lstrlenW(lpcwFileName) == 0) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
LPCWSTR lpcwExtension = ::PathFindExtensionW(lpcwFileName);
|
||||
if (lstrlenW(lpcwExtension) == 0) {
|
||||
return MK_E_INVALIDEXTENSION; // ERROR_MRM_INVALID_FILE_TYPE
|
||||
}
|
||||
|
||||
if (lstrcmpiW(lpcwExtension, L".bmp") == 0)
|
||||
{
|
||||
RESET_POINTER_EX(pRetVal, GUID_ContainerFormatBmp);
|
||||
}
|
||||
else if ((lstrcmpiW(lpcwExtension, L".tif") == 0) ||
|
||||
(lstrcmpiW(lpcwExtension, L".tiff") == 0))
|
||||
{
|
||||
RESET_POINTER_EX(pRetVal, GUID_ContainerFormatTiff);
|
||||
}
|
||||
else if (lstrcmpiW(lpcwExtension, L".png") == 0)
|
||||
{
|
||||
RESET_POINTER_EX(pRetVal, GUID_ContainerFormatPng);
|
||||
}
|
||||
else if ((lstrcmpiW(lpcwExtension, L".jpg") == 0) ||
|
||||
(lstrcmpiW(lpcwExtension, L".jpeg") == 0))
|
||||
{
|
||||
RESET_POINTER_EX(pRetVal, GUID_ContainerFormatJpeg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ERROR_MRM_INVALID_FILE_TYPE;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
COM_DECLSPEC_NOTHROW
|
||||
inline
|
||||
HRESULT
|
||||
SaveImageToFile(
|
||||
_In_ IWICImagingFactory *pWICImagingFactory,
|
||||
_In_ IWICBitmapSource *pWICBitmapSource,
|
||||
_In_ LPCWSTR lpcwFileName
|
||||
)
|
||||
{
|
||||
CHECK_POINTER_EX(pWICImagingFactory, E_INVALIDARG);
|
||||
CHECK_POINTER_EX(pWICBitmapSource, E_INVALIDARG);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
GUID guidContainerFormat;
|
||||
|
||||
hr = GetContainerFormatByFileName(lpcwFileName, &guidContainerFormat);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
WICPixelFormatGUID format = GUID_WICPixelFormatDontCare;
|
||||
CComPtr<IWICImagingFactory> ipWICImagingFactory(pWICImagingFactory);
|
||||
CComPtr<IWICBitmapSource> ipWICBitmapSource(pWICBitmapSource);
|
||||
CComPtr<IWICStream> ipStream;
|
||||
CComPtr<IWICBitmapEncoder> ipEncoder;
|
||||
CComPtr<IWICBitmapFrameEncode> ipFrameEncode;
|
||||
unsigned int uiWidth = 0;
|
||||
unsigned int uiHeight = 0;
|
||||
|
||||
hr = ipWICImagingFactory->CreateStream(&ipStream);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = ipStream->InitializeFromFilename(lpcwFileName, GENERIC_WRITE);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = ipWICImagingFactory->CreateEncoder(guidContainerFormat, NULL, &ipEncoder);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipEncoder->Initialize(ipStream, WICBitmapEncoderNoCache);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipEncoder->CreateNewFrame(&ipFrameEncode, NULL);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipFrameEncode->Initialize(NULL);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipWICBitmapSource->GetSize(&uiWidth, &uiHeight);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipFrameEncode->SetSize(uiWidth, uiHeight);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipFrameEncode->SetPixelFormat(&format);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipFrameEncode->WriteSource(ipWICBitmapSource, NULL);
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipFrameEncode->Commit();
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ipEncoder->Commit();
|
||||
}
|
||||
|
||||
return hr;
|
||||
} // SaveImageToFile
|
||||
|
||||
}; // end class DXGICaptureHelper
|
||||
|
||||
#endif // __DXGICAPTUREHELPER_H__
|
|
@ -0,0 +1,150 @@
|
|||
/*****************************************************************************
|
||||
* DXGICaptureTypes.h
|
||||
*
|
||||
* Copyright (C) 2020 Gokhan Erdogdu <gokhan_erdogdu - at - yahoo - dot - com>
|
||||
*
|
||||
* DXGICapture is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* DXGICapture is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
******************************************************************************/
|
||||
#pragma once
|
||||
#ifndef __DXGICAPTURETYPES_H__
|
||||
#define __DXGICAPTURETYPES_H__
|
||||
|
||||
#include <dxgi1_2.h>
|
||||
#include <windef.h>
|
||||
#include <sal.h>
|
||||
#include <vector>
|
||||
|
||||
//
|
||||
// enum tagFrameSizeMode_e
|
||||
//
|
||||
typedef enum tagFrameSizeMode_e : UINT
|
||||
{
|
||||
tagFrameSizeMode_Normal = 0x0,
|
||||
tagFrameSizeMode_StretchImage = 0x1,
|
||||
tagFrameSizeMode_AutoSize = 0x2,
|
||||
tagFrameSizeMode_CenterImage = 0x3,
|
||||
tagFrameSizeMode_Zoom = 0x4,
|
||||
} tagFrameSizeMode;
|
||||
|
||||
//
|
||||
// enum tagFrameRotationMode_e
|
||||
//
|
||||
typedef enum tagFrameRotationMode_e : UINT
|
||||
{
|
||||
tagFrameRotationMode_Auto = 0x0,
|
||||
tagFrameRotationMode_Identity = 0x1,
|
||||
tagFrameRotationMode_90 = 0x2,
|
||||
tagFrameRotationMode_180 = 0x3,
|
||||
tagFrameRotationMode_270 = 0x4,
|
||||
} tagFrameRotationMode;
|
||||
|
||||
//
|
||||
// Holds info about the pointer/cursor
|
||||
// struct tagMouseInfo_s
|
||||
//
|
||||
typedef struct tagMouseInfo_s
|
||||
{
|
||||
UINT ShapeBufferSize;
|
||||
_Field_size_bytes_(ShapeBufferSize) BYTE* PtrShapeBuffer;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO ShapeInfo;
|
||||
POINT Position;
|
||||
bool Visible;
|
||||
UINT WhoUpdatedPositionLast;
|
||||
LARGE_INTEGER LastTimeStamp;
|
||||
} tagMouseInfo;
|
||||
|
||||
//
|
||||
// struct tagFrameSize_s
|
||||
//
|
||||
typedef struct tagFrameSize_s
|
||||
{
|
||||
LONG Width;
|
||||
LONG Height;
|
||||
} tagFrameSize;
|
||||
|
||||
//
|
||||
// struct tagBounds_s
|
||||
//
|
||||
typedef struct tagFrameBounds_s
|
||||
{
|
||||
LONG X;
|
||||
LONG Y;
|
||||
LONG Width;
|
||||
LONG Height;
|
||||
} tagFrameBounds;
|
||||
|
||||
//
|
||||
// struct tagFrameBufferInfo_s
|
||||
//
|
||||
typedef struct tagFrameBufferInfo_s
|
||||
{
|
||||
UINT BufferSize;
|
||||
_Field_size_bytes_(BufferSize) BYTE* Buffer;
|
||||
INT BytesPerPixel;
|
||||
tagFrameBounds Bounds;
|
||||
INT Pitch;
|
||||
} tagFrameBufferInfo;
|
||||
|
||||
//
|
||||
// struct tagDublicatorMonitorInfo_s
|
||||
//
|
||||
typedef struct tagDublicatorMonitorInfo_s
|
||||
{
|
||||
INT Idx;
|
||||
WCHAR DisplayName[64];
|
||||
INT RotationDegrees;
|
||||
tagFrameBounds Bounds;
|
||||
} tagDublicatorMonitorInfo;
|
||||
|
||||
typedef std::vector<tagDublicatorMonitorInfo*> DublicatorMonitorInfoVec;
|
||||
|
||||
//
|
||||
// struct tagScreenCaptureFilterConfig_s
|
||||
//
|
||||
typedef struct tagScreenCaptureFilterConfig_s
|
||||
{
|
||||
public:
|
||||
INT MonitorIdx;
|
||||
INT ShowCursor;
|
||||
tagFrameRotationMode RotationMode;
|
||||
tagFrameSizeMode SizeMode;
|
||||
tagFrameSize OutputSize; /* Discard for tagFrameSizeMode_AutoSize */
|
||||
} tagScreenCaptureFilterConfig;
|
||||
|
||||
//
|
||||
// struct tagRendererInfo_s
|
||||
//
|
||||
typedef struct tagRendererInfo_s
|
||||
{
|
||||
INT MonitorIdx;
|
||||
INT ShowCursor;
|
||||
tagFrameRotationMode RotationMode;
|
||||
tagFrameSizeMode SizeMode;
|
||||
tagFrameSize OutputSize;
|
||||
|
||||
FLOAT RotationDegrees;
|
||||
FLOAT ScaleX;
|
||||
FLOAT ScaleY;
|
||||
DXGI_FORMAT SrcFormat;
|
||||
tagFrameBounds SrcBounds;
|
||||
tagFrameBounds DstBounds;
|
||||
} tagRendererInfo;
|
||||
|
||||
// macros
|
||||
#define RESET_POINTER_EX(p, v) if (nullptr != (p)) { *(p) = (v); }
|
||||
#define RESET_POINTER(p) RESET_POINTER_EX(p, nullptr)
|
||||
#define CHECK_POINTER_EX(p, hr) if (nullptr == (p)) { return (hr); }
|
||||
#define CHECK_POINTER(p) CHECK_POINTER_EX(p, E_POINTER)
|
||||
#define CHECK_HR_BREAK(hr) if (FAILED(hr)) { break; }
|
||||
#define CHECK_HR_RETURN(hr) { HRESULT hr_379f4648 = hr; if (FAILED(hr_379f4648)) { return hr_379f4648; } }
|
||||
|
||||
#endif // __DXGICAPTURETYPES_H__
|
|
@ -14,7 +14,6 @@ extern "C"
|
|||
};
|
||||
#include <iostream>
|
||||
#else
|
||||
//Linux...
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
|
|
@ -122,9 +122,9 @@ int CaptureAudioFfmpeg::InitCapture(wstring url, uint16_t rate, uint8_t channel)
|
|||
return -1;
|
||||
}
|
||||
//av_dump_format(infmt_ctx, 0, fileAudioInput.c_str(), 1);
|
||||
//END输入文件
|
||||
//END输入文件
|
||||
|
||||
//打开解码器
|
||||
//打开解码器
|
||||
static AVCodec* decodec = avcodec_find_decoder(mInfmt_ctx->streams[0]->codec->codec_id);
|
||||
if (!decodec) {
|
||||
printf("failed find decoder\n");
|
||||
|
@ -134,11 +134,11 @@ int CaptureAudioFfmpeg::InitCapture(wstring url, uint16_t rate, uint8_t channel)
|
|||
printf("failed open decoder\n");
|
||||
return -1;
|
||||
}
|
||||
//END解码器
|
||||
//重采样初始化
|
||||
//END解码器
|
||||
//重采样初始化
|
||||
initAudioFilters();
|
||||
//END重采样初始化
|
||||
//编码器
|
||||
//END重采样初始化
|
||||
//编码器
|
||||
static AVCodec* codec = NULL;
|
||||
//codec = avcodec_find_encoder_by_name("libmp3lame");
|
||||
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
|
||||
|
@ -161,8 +161,8 @@ int CaptureAudioFfmpeg::InitCapture(wstring url, uint16_t rate, uint8_t channel)
|
|||
avcodec_free_context(&codec_ctx);
|
||||
return -1;
|
||||
}
|
||||
//END编码器
|
||||
//输出文件
|
||||
//END编码器
|
||||
//输出文件
|
||||
AVFormatContext* outfmt_ctx = NULL;
|
||||
if (0 > avformat_alloc_output_context2(&outfmt_ctx, NULL, NULL, "aac.aac")) {
|
||||
printf("failed alloc outputcontext\n");
|
||||
|
@ -188,7 +188,7 @@ int CaptureAudioFfmpeg::InitCapture(wstring url, uint16_t rate, uint8_t channel)
|
|||
return -1;
|
||||
}
|
||||
avformat_write_header(outfmt_ctx, NULL);
|
||||
//END输出文件
|
||||
//END输出文件
|
||||
#if 0
|
||||
AVFrame* Frame = av_frame_alloc();
|
||||
Frame->nb_samples = codec_ctx->frame_size;
|
||||
|
@ -288,7 +288,8 @@ int CaptureAudioFfmpeg::initAudioFilters()
|
|||
sprintf_s(args, sizeof(args),
|
||||
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
|
||||
time_base.num, time_base.den, audioDecoderContext->sample_rate,
|
||||
av_get_sample_fmt_name(audioDecoderContext->sample_fmt), audioDecoderContext->channel_layout);
|
||||
av_get_sample_fmt_name(audioDecoderContext->sample_fmt),
|
||||
audioDecoderContext->channel_layout);
|
||||
|
||||
ret = avfilter_graph_create_filter(&mBuffersrcCtx, abuffersrc, "in",
|
||||
args, NULL, mFilterGraph);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#include "screen_capture.h"
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdio.h>
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
|
||||
ScreenCapture::ScreenCapture()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)
|
||||
{
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize=sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(hMonitor,&mi);
|
||||
qDebug()<<QString::asprintf("Device name:%s\t",mi.szDevice);
|
||||
if(mi.dwFlags==MONITORINFOF_PRIMARY) printf("Primary monitor!\n");
|
||||
else printf("\n");
|
||||
qDebug()<<QString::asprintf("Monitor rectangle:(%d,%d,%d,%d)\n",mi.rcMonitor.left,mi.rcMonitor.top,
|
||||
mi.rcMonitor.right,mi.rcMonitor.bottom);
|
||||
qDebug()<<QString::asprintf("Work rectangle:(%d,%d,%d,%d)\n",mi.rcWork.left,mi.rcWork.top,
|
||||
mi.rcWork.right,mi.rcWork.bottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScreenCapture::EnumScreen()
|
||||
{
|
||||
EnumDisplayMonitors(NULL,NULL,MonitorEnumProc,NULL);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef SCREEN_CAPTURE
|
||||
#define SCREEN_CAPTURE
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
class ScreenCapture
|
||||
{
|
||||
public:
|
||||
ScreenCapture();
|
||||
void EnumScreen();
|
||||
};
|
||||
|
||||
#endif // SCREEN_CAPTURE_H
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
#include <Unknwn.h>
|
||||
#include <strmif.h>
|
||||
|
||||
#pragma comment(lib, "strmiids.lib")
|
||||
|
||||
#ifndef __qedit_h__
|
||||
#define __qedit_h__
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct __declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85"))
|
||||
ISampleGrabberCB : IUnknown
|
||||
{
|
||||
//
|
||||
// Raw methods provided by interface
|
||||
//
|
||||
|
||||
virtual HRESULT __stdcall SampleCB(
|
||||
double SampleTime,
|
||||
struct IMediaSample * pSample) = 0;
|
||||
virtual HRESULT __stdcall BufferCB(
|
||||
double SampleTime,
|
||||
unsigned char * pBuffer,
|
||||
long BufferLen) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f"))
|
||||
ISampleGrabber : IUnknown
|
||||
{
|
||||
//
|
||||
// Raw methods provided by interface
|
||||
//
|
||||
|
||||
virtual HRESULT __stdcall SetOneShot(
|
||||
long OneShot) = 0;
|
||||
virtual HRESULT __stdcall SetMediaType(
|
||||
struct _AMMediaType * pType) = 0;
|
||||
virtual HRESULT __stdcall GetConnectedMediaType(
|
||||
struct _AMMediaType * pType) = 0;
|
||||
virtual HRESULT __stdcall SetBufferSamples(
|
||||
long BufferThem) = 0;
|
||||
virtual HRESULT __stdcall GetCurrentBuffer(
|
||||
/*[in,out]*/ long * pBufferSize,
|
||||
/*[out]*/ long * pBuffer) = 0;
|
||||
virtual HRESULT __stdcall GetCurrentSample(
|
||||
/*[out,retval]*/ struct IMediaSample * * ppSample) = 0;
|
||||
virtual HRESULT __stdcall SetCallback(
|
||||
struct ISampleGrabberCB * pCallback,
|
||||
long WhichMethodToCallback) = 0;
|
||||
};
|
||||
|
||||
|
||||
static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce,{ 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
|
||||
static const IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994,{ 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } };
|
||||
static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3,{ 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
||||
static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3,{ 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
|
||||
|
||||
#endif
|
|
@ -0,0 +1,161 @@
|
|||
/********************************************************************************
|
||||
** Form generated from reading UI file 'mainwindow.ui'
|
||||
**
|
||||
** Created by: Qt User Interface Compiler version 5.14.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef UI_MAINWINDOW_H
|
||||
#define UI_MAINWINDOW_H
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QGridLayout>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QSpacerItem>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Ui_MainWindow
|
||||
{
|
||||
public:
|
||||
QWidget *centralWidget;
|
||||
QGridLayout *gridLayout;
|
||||
QVBoxLayout *verticalLayout;
|
||||
QHBoxLayout *horizontalLayout;
|
||||
QPushButton *pushButton;
|
||||
QComboBox *comboBox;
|
||||
QPushButton *pushButton_3;
|
||||
QComboBox *comboBox_2;
|
||||
QLabel *label;
|
||||
QLineEdit *lineEdit;
|
||||
QPushButton *pushButton_2;
|
||||
QSpacerItem *horizontalSpacer_2;
|
||||
QSpacerItem *verticalSpacer;
|
||||
|
||||
void setupUi(QMainWindow *MainWindow)
|
||||
{
|
||||
if (MainWindow->objectName().isEmpty())
|
||||
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
|
||||
MainWindow->resize(1383, 1116);
|
||||
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(MainWindow->sizePolicy().hasHeightForWidth());
|
||||
MainWindow->setSizePolicy(sizePolicy);
|
||||
MainWindow->setMinimumSize(QSize(600, 800));
|
||||
centralWidget = new QWidget(MainWindow);
|
||||
centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
|
||||
QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
sizePolicy1.setHorizontalStretch(0);
|
||||
sizePolicy1.setVerticalStretch(0);
|
||||
sizePolicy1.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth());
|
||||
centralWidget->setSizePolicy(sizePolicy1);
|
||||
gridLayout = new QGridLayout(centralWidget);
|
||||
gridLayout->setSpacing(6);
|
||||
gridLayout->setContentsMargins(11, 11, 11, 11);
|
||||
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
|
||||
verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->setSpacing(6);
|
||||
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
|
||||
horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->setSpacing(6);
|
||||
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
|
||||
horizontalLayout->setContentsMargins(2, 2, 2, 1);
|
||||
pushButton = new QPushButton(centralWidget);
|
||||
pushButton->setObjectName(QString::fromUtf8("pushButton"));
|
||||
pushButton->setMinimumSize(QSize(100, 50));
|
||||
pushButton->setSizeIncrement(QSize(0, 6));
|
||||
pushButton->setBaseSize(QSize(0, 50));
|
||||
|
||||
horizontalLayout->addWidget(pushButton);
|
||||
|
||||
comboBox = new QComboBox(centralWidget);
|
||||
comboBox->setObjectName(QString::fromUtf8("comboBox"));
|
||||
comboBox->setMinimumSize(QSize(200, 35));
|
||||
comboBox->setBaseSize(QSize(0, 50));
|
||||
|
||||
horizontalLayout->addWidget(comboBox);
|
||||
|
||||
pushButton_3 = new QPushButton(centralWidget);
|
||||
pushButton_3->setObjectName(QString::fromUtf8("pushButton_3"));
|
||||
pushButton_3->setMinimumSize(QSize(100, 50));
|
||||
|
||||
horizontalLayout->addWidget(pushButton_3);
|
||||
|
||||
comboBox_2 = new QComboBox(centralWidget);
|
||||
comboBox_2->setObjectName(QString::fromUtf8("comboBox_2"));
|
||||
comboBox_2->setMinimumSize(QSize(200, 35));
|
||||
|
||||
horizontalLayout->addWidget(comboBox_2);
|
||||
|
||||
label = new QLabel(centralWidget);
|
||||
label->setObjectName(QString::fromUtf8("label"));
|
||||
|
||||
horizontalLayout->addWidget(label);
|
||||
|
||||
lineEdit = new QLineEdit(centralWidget);
|
||||
lineEdit->setObjectName(QString::fromUtf8("lineEdit"));
|
||||
lineEdit->setMinimumSize(QSize(300, 30));
|
||||
|
||||
horizontalLayout->addWidget(lineEdit);
|
||||
|
||||
pushButton_2 = new QPushButton(centralWidget);
|
||||
pushButton_2->setObjectName(QString::fromUtf8("pushButton_2"));
|
||||
pushButton_2->setMinimumSize(QSize(60, 50));
|
||||
|
||||
horizontalLayout->addWidget(pushButton_2);
|
||||
|
||||
horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
||||
horizontalLayout->addItem(horizontalSpacer_2);
|
||||
|
||||
horizontalLayout->setStretch(0, 1);
|
||||
horizontalLayout->setStretch(1, 2);
|
||||
horizontalLayout->setStretch(7, 13);
|
||||
|
||||
verticalLayout->addLayout(horizontalLayout);
|
||||
|
||||
verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
|
||||
verticalLayout->addItem(verticalSpacer);
|
||||
|
||||
verticalLayout->setStretch(0, 1);
|
||||
verticalLayout->setStretch(1, 9);
|
||||
|
||||
gridLayout->addLayout(verticalLayout, 0, 0, 1, 1);
|
||||
|
||||
MainWindow->setCentralWidget(centralWidget);
|
||||
|
||||
retranslateUi(MainWindow);
|
||||
|
||||
QMetaObject::connectSlotsByName(MainWindow);
|
||||
} // setupUi
|
||||
|
||||
void retranslateUi(QMainWindow *MainWindow)
|
||||
{
|
||||
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
|
||||
pushButton->setText(QCoreApplication::translate("MainWindow", "\346\211\223\345\274\200\346\221\204\345\203\217\345\244\264", nullptr));
|
||||
pushButton_3->setText(QCoreApplication::translate("MainWindow", "\346\211\223\345\274\200\351\272\246\345\205\213\351\243\216", nullptr));
|
||||
label->setText(QCoreApplication::translate("MainWindow", "rtmp\346\216\250\346\265\201\345\234\260\345\235\200", nullptr));
|
||||
lineEdit->setText(QCoreApplication::translate("MainWindow", "rtmp://127.0.0.1:1935/live/1", nullptr));
|
||||
pushButton_2->setText(QCoreApplication::translate("MainWindow", "\346\216\250\346\265\201", nullptr));
|
||||
} // retranslateUi
|
||||
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow: public Ui_MainWindow {};
|
||||
} // namespace Ui
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // UI_MAINWINDOW_H
|
|
@ -0,0 +1,63 @@
|
|||
/********************************************************************************
|
||||
** Form generated from reading UI file 'process.ui'
|
||||
**
|
||||
** Created by: Qt User Interface Compiler version 5.14.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef UI_PROCESS_H
|
||||
#define UI_PROCESS_H
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QProgressBar>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Ui_Process
|
||||
{
|
||||
public:
|
||||
QProgressBar *progressBar;
|
||||
QLabel *label;
|
||||
|
||||
void setupUi(QDialog *Process)
|
||||
{
|
||||
if (Process->objectName().isEmpty())
|
||||
Process->setObjectName(QString::fromUtf8("Process"));
|
||||
Process->resize(324, 88);
|
||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(Process->sizePolicy().hasHeightForWidth());
|
||||
Process->setSizePolicy(sizePolicy);
|
||||
progressBar = new QProgressBar(Process);
|
||||
progressBar->setObjectName(QString::fromUtf8("progressBar"));
|
||||
progressBar->setGeometry(QRect(30, 50, 281, 31));
|
||||
progressBar->setValue(24);
|
||||
label = new QLabel(Process);
|
||||
label->setObjectName(QString::fromUtf8("label"));
|
||||
label->setGeometry(QRect(120, 30, 121, 16));
|
||||
|
||||
retranslateUi(Process);
|
||||
|
||||
QMetaObject::connectSlotsByName(Process);
|
||||
} // setupUi
|
||||
|
||||
void retranslateUi(QDialog *Process)
|
||||
{
|
||||
Process->setWindowTitle(QCoreApplication::translate("Process", "Dialog", nullptr));
|
||||
label->setText(QCoreApplication::translate("Process", "\346\255\243\345\234\250\345\212\240\350\275\275\346\225\260\346\215\256", nullptr));
|
||||
} // retranslateUi
|
||||
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class Process: public Ui_Process {};
|
||||
} // namespace Ui
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // UI_PROCESS_H
|
|
@ -0,0 +1,55 @@
|
|||
/********************************************************************************
|
||||
** Form generated from reading UI file 'qsstoast.ui'
|
||||
**
|
||||
** Created by: Qt User Interface Compiler version 5.14.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef UI_QSSTOAST_H
|
||||
#define UI_QSSTOAST_H
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Ui_Toast
|
||||
{
|
||||
public:
|
||||
QLabel *label;
|
||||
|
||||
void setupUi(QWidget *Toast)
|
||||
{
|
||||
if (Toast->objectName().isEmpty())
|
||||
Toast->setObjectName(QString::fromUtf8("Toast"));
|
||||
Toast->resize(932, 59);
|
||||
QFont font;
|
||||
font.setFamily(QString::fromUtf8("Arial"));
|
||||
Toast->setFont(font);
|
||||
label = new QLabel(Toast);
|
||||
label->setObjectName(QString::fromUtf8("label"));
|
||||
label->setGeometry(QRect(170, 10, 231, 31));
|
||||
|
||||
retranslateUi(Toast);
|
||||
|
||||
QMetaObject::connectSlotsByName(Toast);
|
||||
} // setupUi
|
||||
|
||||
void retranslateUi(QWidget *Toast)
|
||||
{
|
||||
Toast->setWindowTitle(QCoreApplication::translate("Toast", "Form", nullptr));
|
||||
label->setText(QCoreApplication::translate("Toast", "TextLabel", nullptr));
|
||||
} // retranslateUi
|
||||
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class Toast: public Ui_Toast {};
|
||||
} // namespace Ui
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // UI_QSSTOAST_H
|
|
@ -0,0 +1,52 @@
|
|||
/********************************************************************************
|
||||
** Form generated from reading UI file 'toast.ui'
|
||||
**
|
||||
** Created by: Qt User Interface Compiler version 5.14.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef UI_TOAST_H
|
||||
#define UI_TOAST_H
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Ui_Form
|
||||
{
|
||||
public:
|
||||
QLabel *label;
|
||||
|
||||
void setupUi(QWidget *Form)
|
||||
{
|
||||
if (Form->objectName().isEmpty())
|
||||
Form->setObjectName(QString::fromUtf8("Form"));
|
||||
Form->resize(932, 59);
|
||||
label = new QLabel(Form);
|
||||
label->setObjectName(QString::fromUtf8("label"));
|
||||
label->setGeometry(QRect(170, 10, 231, 31));
|
||||
|
||||
retranslateUi(Form);
|
||||
|
||||
QMetaObject::connectSlotsByName(Form);
|
||||
} // setupUi
|
||||
|
||||
void retranslateUi(QWidget *Form)
|
||||
{
|
||||
Form->setWindowTitle(QCoreApplication::translate("Form", "Form", nullptr));
|
||||
label->setText(QCoreApplication::translate("Form", "TextLabel", nullptr));
|
||||
} // retranslateUi
|
||||
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class Form: public Ui_Form {};
|
||||
} // namespace Ui
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // UI_TOAST_H
|
|
@ -42,40 +42,75 @@ SOURCES += \
|
|||
media/AACAudioCoder.cpp \
|
||||
media/AudioCapture.cpp \
|
||||
media/CameraCapture.cpp \
|
||||
media/DXGICapture.cpp \
|
||||
media/RtmpPusher.cpp \
|
||||
media/VideoCoder.cpp \
|
||||
media/audiocaptureff.cpp \
|
||||
media/screen_capture.cpp \
|
||||
media/sps_decode.cpp \
|
||||
utils/Base64.cpp \
|
||||
utils/Debuger.cpp \
|
||||
utils/utils.cpp
|
||||
HEADERS += \
|
||||
components/toast.h \
|
||||
librtmp/strncasecmp.h \
|
||||
mainwindow.h \
|
||||
cplaywidget.h \
|
||||
cplaywidget.h \
|
||||
media/audiocaptureff.h
|
||||
media/screen_capture.h
|
||||
|
||||
|
||||
FORMS += \
|
||||
components/toast.ui \
|
||||
mainwindow.ui
|
||||
INCLUDEPATH += media/ third/ffmpeg/include/ inc/ third/
|
||||
|
||||
|
||||
INCLUDEPATH += media/
|
||||
contains(DEFINES, __MINGW32__){
|
||||
message("sfasdfdsf")
|
||||
LIBS += -L$$PWD/third/libs/
|
||||
LIBS += -lm -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswresample -lswscale -lpthread -lm -lfdk-aac -lx264 -liconv -lucrtbase -lstrmiids
|
||||
LIBS += -lole32 -loleAut32 -lquartz -ldxguid -ldxapi -lwinmm -lbcrypt -lssl -lcrypto -lGdi32 -lws2_32 -lbz2 -lz -lportaudio -lshlwapi -lvfw32 -lpostproc -luuid
|
||||
}else{
|
||||
message("111")
|
||||
LIBS += -L$$PWD/third/libs/
|
||||
LIBS += -lm -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswresample -lswscale -lpthread -lm -lfdk-aac -lx264 -liconv -lucrtbase -lstrmiids
|
||||
LIBS += -lole32 -loleAut32 -lquartz -ldxguid -ldxapi -lwinmm -lbcrypt -lssl -lcrypto -lGdi32 -lz -lportaudio -lshlwapi -lvfw32 -lpostproc -luuid
|
||||
message("mingw")
|
||||
INCLUDEPATH += media/ inc/
|
||||
contains(QT_ARCH, i386) {
|
||||
message("32-bit")
|
||||
LIBS += -L$$PWD/third/ffmpeg/mingw/32/lib
|
||||
LIBS += -lm -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswresample -lswscale -lpthread -lm -lfdk-aac -lx264 -liconv -lucrtbase -lstrmiids
|
||||
LIBS += -lole32 -loleAut32 -lquartz -ldxguid -ldxapi -lwinmm -lbcrypt -lssl -lcrypto -lGdi32 -lws2_32 -lbz2 -lz -lportaudio -lshlwapi -lvfw32 -lpostproc -luuid
|
||||
} else {
|
||||
message("64-bit")
|
||||
}
|
||||
}else{
|
||||
message("msvc")
|
||||
|
||||
|
||||
DEFINES += _CRT_SECURE_NO_DEPRECATE \
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
|
||||
|
||||
contains(QT_ARCH, i386) {
|
||||
INCLUDEPATH += inc $$PWD/third/msvc32/fdk-aac/include \
|
||||
$$PWD/third/msvc32/libx264/include \
|
||||
$$PWD/third/msvc32/ffmpeg/include \
|
||||
$$PWD/third/msvc32/openssl/include
|
||||
|
||||
LIBS += -L$$PWD/third/msvc32/libx264/lib
|
||||
LIBS += -L$$PWD/third/msvc32/fdk-aac/lib
|
||||
LIBS += -L$$PWD/third/msvc32/ffmpeg/lib
|
||||
LIBS += -L$$PWD/third/msvc32/openssl/lib
|
||||
|
||||
LIBS += libavfilter.a libavdevice.a libavcodec.a libpostproc.a \
|
||||
libavformat.a libavutil.a \
|
||||
libswresample.a libswscale.a fdk-aac.lib ws2_32.lib libeay32.lib ssleay32.lib \
|
||||
shell32.lib gdi32.lib crypt32.lib User32.lib GDI32.lib Advapi32.lib zlibstaticd.lib Secur32.lib \
|
||||
Bcrypt.lib Kernel32.lib portaudio_x86.lib ole32.lib oleaut32.lib strmiids.lib libx264.lib
|
||||
|
||||
}
|
||||
else{
|
||||
message("64-bit")
|
||||
|
||||
QMAKE_CXXFLAGS_RELEASE += -Zi
|
||||
QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QMAKE_CXXFLAGS_RELEASE += -Zi
|
||||
QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF
|
||||
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.11.0, 2021-10-02T02:21:21. -->
|
||||
<!-- Written by QtCreator 4.11.0, 2021-10-04T20:14:49. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
@ -71,7 +71,7 @@
|
|||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">D:/project/multimedia/client/qt_gl_/yuvgl</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">D:/project/multimedia/client/qt_gl_/build-yuvgl-Desktop_Qt_5_14_0_MSVC2017_32bit-Debug</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
|
@ -299,7 +299,7 @@
|
|||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default">D:/project/multimedia/client/qt_gl_/yuvgl</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default">D:/project/multimedia/client/qt_gl_/build-yuvgl-Desktop_Qt_5_14_0_MSVC2017_32bit-Debug</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue