添加屏幕捕获功能
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.4.10-pre1
|
||||||
client/qt_gl_/yuvgl/yuvgl.pro.user.bccf4b5
|
client/qt_gl_/yuvgl/yuvgl.pro.user.bccf4b5
|
||||||
client/qt_gl_/yuvgl/yuvgl.pro.user.ed68183.4.8-pre1
|
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;
|
extern TLS_CTX RTMP_TLS_ctx;
|
||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#include "strncasecmp.h"
|
||||||
|
|
||||||
#endif /* CRYPTO */
|
#endif /* CRYPTO */
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "rtmp_sys.h"
|
#include "rtmp_sys.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "strncasecmp.h"
|
||||||
|
|
||||||
int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
|
int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
|
||||||
AVal *playpath, AVal *app)
|
AVal *playpath, AVal *app)
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "rtmp_sys.h"
|
#include "rtmp_sys.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "strncasecmp.h"
|
||||||
|
|
||||||
#ifdef CRYPTO
|
#ifdef CRYPTO
|
||||||
#ifdef USE_POLARSSL
|
#ifdef USE_POLARSSL
|
||||||
|
@ -1165,7 +1166,7 @@ RTMP_ToggleStream(RTMP *r)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
r->m_pausing = 1;
|
r->m_pausing = 1;
|
||||||
sleep(1);
|
msleep(1000);
|
||||||
}
|
}
|
||||||
res = RTMP_SendPause(r, FALSE, r->m_pauseStamp);
|
res = RTMP_SendPause(r, FALSE, r->m_pauseStamp);
|
||||||
r->m_pausing = 3;
|
r->m_pausing = 3;
|
||||||
|
@ -1524,10 +1525,10 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *packet)
|
||||||
return bHasMediaPacket;
|
return bHasMediaPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
//#ifdef _DEBUG
|
||||||
extern FILE *netstackdump;
|
//extern FILE *netstackdump;
|
||||||
extern FILE *netstackdump_read;
|
//extern FILE *netstackdump_read;
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
// @remark debug info by http://github.com/ossrs/srs
|
// @remark debug info by http://github.com/ossrs/srs
|
||||||
unsigned long _srs_rbytes = 0;
|
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); */
|
/*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
fwrite(ptr, 1, nBytes, netstackdump_read);
|
// fwrite(ptr, 1, nBytes, netstackdump_read);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (nBytes == 0)
|
if (nBytes == 0)
|
||||||
|
@ -4469,7 +4470,7 @@ RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len)
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
fwrite(buf, 1, len, netstackdump);
|
// fwrite(buf, 1, len, netstackdump);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CRYPTO) && !defined(NO_SSL)
|
#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 <qlibrary.h>
|
||||||
#include <qsysinfo.h>
|
#include <qsysinfo.h>
|
||||||
#include <qt_windows.h>
|
#include <qt_windows.h>
|
||||||
|
#include "media/screen_capture.h"
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
#include <Tlhelp32.h>
|
#include <Tlhelp32.h>
|
||||||
#include "winuser.h"
|
#include "winuser.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int RegiesterOwnType(){
|
int RegiesterOwnType(){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
ScreenCapture g;
|
||||||
|
g.EnumScreen();
|
||||||
|
|
||||||
QssEventFilter filter;
|
QssEventFilter filter;
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
MainWindow main;
|
MainWindow main;
|
||||||
main.setWindowTitle("视频采集rtmp推流工具");
|
main.setWindowTitle("직첵竟꿎桿묏야");
|
||||||
main.setFixedSize(1920,1080);
|
main.setFixedSize(1920,1080);
|
||||||
main.show();
|
main.show();
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>打开摄像头</string>
|
<string>???????</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>打开麦克风</string>
|
<string>??????</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>rtmp推流地址</string>
|
<string>rtmp???????</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -142,7 +142,7 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>推流</string>
|
<string>????</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -15,7 +15,7 @@ void AACAudioCoder::OnAudioData(const void *frameaddress, uint32_t framelen)
|
||||||
AACAudioCoder::AACAudioCoder(unsigned int smprate, unsigned int channel) {
|
AACAudioCoder::AACAudioCoder(unsigned int smprate, unsigned int channel) {
|
||||||
AVCodecID codec_id = AV_CODEC_ID_AAC;
|
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) {
|
if (!pCodec) {
|
||||||
printf("Codec not found\n");
|
printf("Codec not found\n");
|
||||||
this->mStatus = FAIL;
|
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>
|
#include <iostream>
|
||||||
#else
|
#else
|
||||||
//Linux...
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
|
@ -122,9 +122,9 @@ int CaptureAudioFfmpeg::InitCapture(wstring url, uint16_t rate, uint8_t channel)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//av_dump_format(infmt_ctx, 0, fileAudioInput.c_str(), 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);
|
static AVCodec* decodec = avcodec_find_decoder(mInfmt_ctx->streams[0]->codec->codec_id);
|
||||||
if (!decodec) {
|
if (!decodec) {
|
||||||
printf("failed find decoder\n");
|
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");
|
printf("failed open decoder\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//END解码器
|
//END解码器
|
||||||
//重采样初始化
|
//重采样初始化
|
||||||
initAudioFilters();
|
initAudioFilters();
|
||||||
//END重采样初始化
|
//END重采样初始化
|
||||||
//编码器
|
//编码器
|
||||||
static AVCodec* codec = NULL;
|
static AVCodec* codec = NULL;
|
||||||
//codec = avcodec_find_encoder_by_name("libmp3lame");
|
//codec = avcodec_find_encoder_by_name("libmp3lame");
|
||||||
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
|
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);
|
avcodec_free_context(&codec_ctx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
//END编码器
|
//END编码器
|
||||||
//输出文件
|
//输出文件
|
||||||
AVFormatContext* outfmt_ctx = NULL;
|
AVFormatContext* outfmt_ctx = NULL;
|
||||||
if (0 > avformat_alloc_output_context2(&outfmt_ctx, NULL, NULL, "aac.aac")) {
|
if (0 > avformat_alloc_output_context2(&outfmt_ctx, NULL, NULL, "aac.aac")) {
|
||||||
printf("failed alloc outputcontext\n");
|
printf("failed alloc outputcontext\n");
|
||||||
|
@ -188,7 +188,7 @@ int CaptureAudioFfmpeg::InitCapture(wstring url, uint16_t rate, uint8_t channel)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
avformat_write_header(outfmt_ctx, NULL);
|
avformat_write_header(outfmt_ctx, NULL);
|
||||||
//END输出文件
|
//END输出文件
|
||||||
#if 0
|
#if 0
|
||||||
AVFrame* Frame = av_frame_alloc();
|
AVFrame* Frame = av_frame_alloc();
|
||||||
Frame->nb_samples = codec_ctx->frame_size;
|
Frame->nb_samples = codec_ctx->frame_size;
|
||||||
|
@ -288,7 +288,8 @@ int CaptureAudioFfmpeg::initAudioFilters()
|
||||||
sprintf_s(args, sizeof(args),
|
sprintf_s(args, sizeof(args),
|
||||||
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
|
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
|
||||||
time_base.num, time_base.den, audioDecoderContext->sample_rate,
|
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",
|
ret = avfilter_graph_create_filter(&mBuffersrcCtx, abuffersrc, "in",
|
||||||
args, NULL, mFilterGraph);
|
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,39 +42,74 @@ SOURCES += \
|
||||||
media/AACAudioCoder.cpp \
|
media/AACAudioCoder.cpp \
|
||||||
media/AudioCapture.cpp \
|
media/AudioCapture.cpp \
|
||||||
media/CameraCapture.cpp \
|
media/CameraCapture.cpp \
|
||||||
|
media/DXGICapture.cpp \
|
||||||
media/RtmpPusher.cpp \
|
media/RtmpPusher.cpp \
|
||||||
media/VideoCoder.cpp \
|
media/VideoCoder.cpp \
|
||||||
media/audiocaptureff.cpp \
|
media/audiocaptureff.cpp \
|
||||||
|
media/screen_capture.cpp \
|
||||||
media/sps_decode.cpp \
|
media/sps_decode.cpp \
|
||||||
utils/Base64.cpp \
|
utils/Base64.cpp \
|
||||||
utils/Debuger.cpp \
|
utils/Debuger.cpp \
|
||||||
utils/utils.cpp
|
utils/utils.cpp
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
components/toast.h \
|
components/toast.h \
|
||||||
|
librtmp/strncasecmp.h \
|
||||||
mainwindow.h \
|
mainwindow.h \
|
||||||
cplaywidget.h \
|
cplaywidget.h \
|
||||||
cplaywidget.h \
|
media/screen_capture.h
|
||||||
media/audiocaptureff.h
|
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
components/toast.ui \
|
components/toast.ui \
|
||||||
mainwindow.ui
|
mainwindow.ui
|
||||||
INCLUDEPATH += media/ third/ffmpeg/include/ inc/ third/
|
|
||||||
|
|
||||||
|
|
||||||
|
INCLUDEPATH += media/
|
||||||
contains(DEFINES, __MINGW32__){
|
contains(DEFINES, __MINGW32__){
|
||||||
message("sfasdfdsf")
|
message("mingw")
|
||||||
LIBS += -L$$PWD/third/libs/
|
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 += -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
|
LIBS += -lole32 -loleAut32 -lquartz -ldxguid -ldxapi -lwinmm -lbcrypt -lssl -lcrypto -lGdi32 -lws2_32 -lbz2 -lz -lportaudio -lshlwapi -lvfw32 -lpostproc -luuid
|
||||||
} else {
|
} else {
|
||||||
message("111")
|
message("64-bit")
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
}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_CXXFLAGS_RELEASE += -Zi
|
||||||
QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF
|
QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
qnx: target.path = /tmp/$${TARGET}/bin
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!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>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
<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.BuildConfiguration.BuildStepList.0">
|
||||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
<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.UseQmlDebugger">false</value>
|
||||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
<value type="QString" key="RunConfiguration.WorkingDirectory"></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>
|
</valuemap>
|
||||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||||
</valuemap>
|
</valuemap>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue