2017-11-02 06:55:25 +00:00
|
|
|
/*
|
2017-12-03 15:31:16 +00:00
|
|
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
2017-11-02 06:55:25 +00:00
|
|
|
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
2017-12-11 17:30:47 +00:00
|
|
|
https://looking-glass.hostfission.com
|
2017-11-02 06:55:25 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; either version 2 of the License, or (at your option) any later
|
|
|
|
version.
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
|
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2017-12-14 19:11:41 +00:00
|
|
|
#include "Capture/DXGI.h"
|
2017-11-02 06:55:25 +00:00
|
|
|
using namespace Capture;
|
|
|
|
|
2017-12-14 19:11:41 +00:00
|
|
|
#include "common/debug.h"
|
2018-07-23 05:21:43 +00:00
|
|
|
#include "common/memcpySSE.h"
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
DXGI::DXGI() :
|
2017-11-16 09:53:22 +00:00
|
|
|
m_options(NULL),
|
2017-11-02 11:37:19 +00:00
|
|
|
m_initialized(false),
|
2017-12-14 19:18:31 +00:00
|
|
|
m_dxgiFactory(),
|
|
|
|
m_device(),
|
|
|
|
m_deviceContext(),
|
2018-10-03 14:07:34 +00:00
|
|
|
m_dup()
|
2017-11-02 06:55:25 +00:00
|
|
|
{
|
2018-10-03 14:07:34 +00:00
|
|
|
InitializeCriticalSection(&m_cursorCS );
|
|
|
|
InitializeCriticalSection(&m_cursorDataCS);
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DXGI::~DXGI()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-07-23 15:11:19 +00:00
|
|
|
bool DXGI::CanInitialize()
|
|
|
|
{
|
|
|
|
HDESK desktop = OpenInputDesktop(0, TRUE, GENERIC_READ);
|
|
|
|
if (!desktop)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CloseDesktop(desktop);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-16 09:53:22 +00:00
|
|
|
bool DXGI::Initialize(CaptureOptions * options)
|
2017-11-02 06:55:25 +00:00
|
|
|
{
|
|
|
|
if (m_initialized)
|
|
|
|
DeInitialize();
|
|
|
|
|
2017-11-16 09:53:22 +00:00
|
|
|
m_options = options;
|
2017-11-03 11:20:48 +00:00
|
|
|
HRESULT status;
|
|
|
|
|
|
|
|
status = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)(&m_dxgiFactory));
|
|
|
|
if (FAILED(status))
|
2017-11-02 11:37:19 +00:00
|
|
|
{
|
2017-12-14 19:13:07 +00:00
|
|
|
DEBUG_ERROR("Failed to create DXGIFactory: %08x", (int)status);
|
2017-11-02 11:37:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
bool done = false;
|
2017-12-14 19:18:31 +00:00
|
|
|
IDXGIAdapter1Ptr adapter;
|
2017-11-03 11:20:48 +00:00
|
|
|
for (int i = 0; m_dxgiFactory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++)
|
|
|
|
{
|
2017-12-14 19:18:31 +00:00
|
|
|
IDXGIOutputPtr output;
|
2017-11-03 11:20:48 +00:00
|
|
|
for (int i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++)
|
|
|
|
{
|
|
|
|
DXGI_OUTPUT_DESC outputDesc;
|
|
|
|
output->GetDesc(&outputDesc);
|
|
|
|
if (!outputDesc.AttachedToDesktop)
|
|
|
|
{
|
2018-07-28 00:25:57 +00:00
|
|
|
output = NULL;
|
2017-11-03 11:20:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_output = output;
|
|
|
|
if (!m_output)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to get IDXGIOutput1");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-26 11:20:17 +00:00
|
|
|
DXGI_ADAPTER_DESC1 adapterDesc;
|
|
|
|
adapter->GetDesc1(&adapterDesc);
|
2018-10-03 14:07:34 +00:00
|
|
|
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
|
|
|
|
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
|
|
|
|
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
|
|
|
|
DEBUG_INFO("Device Video Mem : %lld MB", adapterDesc.DedicatedVideoMemory / 1048576);
|
|
|
|
DEBUG_INFO("Device Sys Mem : %lld MB", adapterDesc.DedicatedSystemMemory / 1048576);
|
|
|
|
DEBUG_INFO("Shared Sys Mem : %lld MB", adapterDesc.SharedSystemMemory / 1048576);
|
2018-09-26 11:20:17 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
m_width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left;
|
|
|
|
m_height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top;
|
2018-09-26 11:20:17 +00:00
|
|
|
DEBUG_INFO("Capture Size : %u x %u", m_width, m_height);
|
2017-11-03 11:20:48 +00:00
|
|
|
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
|
2018-07-28 00:25:57 +00:00
|
|
|
adapter = NULL;
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
if (!done)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to locate a valid output device");
|
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const D3D_FEATURE_LEVEL featureLevels[] = {
|
2017-12-29 09:53:52 +00:00
|
|
|
D3D_FEATURE_LEVEL_11_1,
|
|
|
|
D3D_FEATURE_LEVEL_11_0,
|
2017-11-03 11:20:48 +00:00
|
|
|
D3D_FEATURE_LEVEL_10_1,
|
|
|
|
D3D_FEATURE_LEVEL_10_0,
|
|
|
|
D3D_FEATURE_LEVEL_9_3,
|
|
|
|
D3D_FEATURE_LEVEL_9_2,
|
|
|
|
D3D_FEATURE_LEVEL_9_1
|
|
|
|
};
|
|
|
|
|
2018-09-26 11:20:17 +00:00
|
|
|
#ifdef _DEBUG
|
2017-11-26 05:10:59 +00:00
|
|
|
#define CREATE_FLAGS (D3D11_CREATE_DEVICE_DEBUG)
|
|
|
|
#else
|
|
|
|
#define CREATE_FLAGS (0)
|
|
|
|
#endif
|
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
status = D3D11CreateDevice(
|
|
|
|
adapter,
|
|
|
|
D3D_DRIVER_TYPE_UNKNOWN,
|
|
|
|
NULL,
|
2017-12-29 09:53:52 +00:00
|
|
|
CREATE_FLAGS | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
|
2017-11-03 11:20:48 +00:00
|
|
|
featureLevels, ARRAYSIZE(featureLevels),
|
|
|
|
D3D11_SDK_VERSION,
|
|
|
|
&m_device,
|
|
|
|
&m_featureLevel,
|
|
|
|
&m_deviceContext
|
|
|
|
);
|
2017-12-29 09:53:52 +00:00
|
|
|
#undef CREATE_FLAGS
|
2017-11-03 11:20:48 +00:00
|
|
|
|
2017-11-03 17:02:55 +00:00
|
|
|
if (FAILED(status))
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WINERROR("Failed to create D3D11 device", status);
|
2017-11-03 11:20:48 +00:00
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-26 11:20:17 +00:00
|
|
|
DEBUG_INFO("Feature Level : 0x%x", m_featureLevel);
|
2018-07-27 16:27:36 +00:00
|
|
|
|
|
|
|
m_frameType = FRAME_TYPE_ARGB;
|
2017-12-29 09:53:52 +00:00
|
|
|
for(CaptureOptions::const_iterator it = m_options->cbegin(); it != m_options->cend(); ++it)
|
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
if (_stricmp(*it, "h264" ) == 0) m_frameType = FRAME_TYPE_H264;
|
|
|
|
if (_stricmp(*it, "yuv420") == 0) m_frameType = FRAME_TYPE_YUV420;
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-27 16:27:36 +00:00
|
|
|
if (m_frameType == FRAME_TYPE_H264)
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WARN("Enabling experimental H.264 compression");
|
2018-07-27 16:27:36 +00:00
|
|
|
|
|
|
|
bool ok = false;
|
|
|
|
switch (m_frameType)
|
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
case FRAME_TYPE_ARGB : ok = InitRawCapture (); break;
|
|
|
|
case FRAME_TYPE_YUV420: ok = InitYUV420Capture(); break;
|
|
|
|
case FRAME_TYPE_H264 : ok = InitH264Capture (); break;
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
2018-07-27 16:27:36 +00:00
|
|
|
|
|
|
|
if (!ok)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-07-27 16:27:36 +00:00
|
|
|
DeInitialize();
|
|
|
|
return false;
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2017-12-18 06:18:37 +00:00
|
|
|
IDXGIDevicePtr dxgi;
|
2017-12-29 09:53:52 +00:00
|
|
|
status = m_device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgi);
|
|
|
|
if (FAILED(status))
|
2017-12-18 06:18:37 +00:00
|
|
|
{
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WINERROR("Failed to obtain the IDXGIDevice interface from the D3D11 device", status);
|
2017-12-18 06:18:37 +00:00
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dxgi->SetGPUThreadPriority(7);
|
|
|
|
|
2017-11-03 17:00:00 +00:00
|
|
|
// we try this twice just incase we still get an error
|
|
|
|
// on re-initialization
|
|
|
|
for(int i = 0; i < 2; ++i)
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
status = m_output->DuplicateOutput(m_device, &m_dup);
|
|
|
|
if (SUCCEEDED(status))
|
|
|
|
break;
|
|
|
|
Sleep(200);
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WINERROR("DuplicateOutput Failed", status);
|
2017-11-03 11:20:48 +00:00
|
|
|
DeInitialize();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-29 09:53:52 +00:00
|
|
|
m_initialized = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DXGI::InitRawCapture()
|
|
|
|
{
|
2017-11-03 11:20:48 +00:00
|
|
|
D3D11_TEXTURE2D_DESC texDesc;
|
|
|
|
ZeroMemory(&texDesc, sizeof(texDesc));
|
|
|
|
texDesc.Width = m_width;
|
|
|
|
texDesc.Height = m_height;
|
|
|
|
texDesc.MipLevels = 1;
|
|
|
|
texDesc.ArraySize = 1;
|
|
|
|
texDesc.SampleDesc.Count = 1;
|
|
|
|
texDesc.SampleDesc.Quality = 0;
|
|
|
|
texDesc.Usage = D3D11_USAGE_STAGING;
|
|
|
|
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
texDesc.BindFlags = 0;
|
|
|
|
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
|
|
texDesc.MiscFlags = 0;
|
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
HRESULT status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture[0]);
|
2017-11-03 11:20:48 +00:00
|
|
|
if (FAILED(status))
|
|
|
|
{
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WINERROR("Failed to create texture", status);
|
2017-11-03 11:20:48 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-12-29 09:53:52 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
bool DXGI::InitYUV420Capture()
|
2018-07-27 16:27:36 +00:00
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
HRESULT status;
|
2018-07-27 16:27:36 +00:00
|
|
|
D3D11_TEXTURE2D_DESC texDesc;
|
2018-07-27 20:15:55 +00:00
|
|
|
|
2018-07-27 16:27:36 +00:00
|
|
|
ZeroMemory(&texDesc, sizeof(texDesc));
|
|
|
|
texDesc.Width = m_width;
|
|
|
|
texDesc.Height = m_height;
|
|
|
|
texDesc.MipLevels = 1;
|
|
|
|
texDesc.ArraySize = 1;
|
|
|
|
texDesc.SampleDesc.Count = 1;
|
|
|
|
texDesc.SampleDesc.Quality = 0;
|
|
|
|
texDesc.Usage = D3D11_USAGE_STAGING;
|
2018-07-27 20:15:55 +00:00
|
|
|
texDesc.Format = DXGI_FORMAT_R8_UNORM;
|
2018-07-27 16:27:36 +00:00
|
|
|
texDesc.BindFlags = 0;
|
|
|
|
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
|
|
texDesc.MiscFlags = 0;
|
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture[0]);
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_WINERROR("Failed to create texture", status);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
texDesc.Width /= 2;
|
|
|
|
texDesc.Height /= 2;
|
|
|
|
|
|
|
|
status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture[1]);
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_WINERROR("Failed to create texture", status);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture[2]);
|
2018-07-27 16:27:36 +00:00
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_WINERROR("Failed to create texture", status);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_textureConverter = new TextureConverter();
|
2018-07-27 20:15:55 +00:00
|
|
|
if (!m_textureConverter->Initialize(m_deviceContext, m_device, m_width, m_height, FRAME_TYPE_YUV420))
|
2018-07-27 16:27:36 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-29 09:53:52 +00:00
|
|
|
bool DXGI::InitH264Capture()
|
|
|
|
{
|
2018-07-27 16:27:36 +00:00
|
|
|
m_textureConverter = new TextureConverter();
|
2018-07-27 20:15:55 +00:00
|
|
|
if (!m_textureConverter->Initialize(m_deviceContext, m_device, m_width, m_height, FRAME_TYPE_YUV420))
|
2018-07-27 16:27:36 +00:00
|
|
|
return false;
|
|
|
|
|
2018-07-25 19:50:06 +00:00
|
|
|
m_h264 = new MFT::H264();
|
|
|
|
if (!m_h264->Initialize(m_device, m_width, m_height))
|
2017-12-29 09:53:52 +00:00
|
|
|
return false;
|
2018-07-25 19:50:06 +00:00
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DXGI::DeInitialize()
|
|
|
|
{
|
2018-07-26 01:13:07 +00:00
|
|
|
if (m_h264)
|
|
|
|
{
|
|
|
|
delete m_h264;
|
|
|
|
m_h264 = NULL;
|
|
|
|
}
|
|
|
|
|
2018-07-27 16:27:36 +00:00
|
|
|
if (m_textureConverter)
|
|
|
|
{
|
|
|
|
delete m_textureConverter;
|
|
|
|
m_textureConverter = NULL;
|
|
|
|
}
|
|
|
|
|
2018-05-31 08:51:28 +00:00
|
|
|
ReleaseFrame();
|
2017-12-18 01:03:22 +00:00
|
|
|
|
2018-10-03 14:07:34 +00:00
|
|
|
while(m_cursorData.size())
|
2017-11-03 13:14:30 +00:00
|
|
|
{
|
2018-10-03 14:07:34 +00:00
|
|
|
CursorBuffer * buf = m_cursorData.front();
|
|
|
|
delete[] buf->buffer;
|
|
|
|
delete buf;
|
|
|
|
m_cursorData.pop_front();
|
2017-11-03 13:14:30 +00:00
|
|
|
}
|
|
|
|
|
2018-07-28 00:25:57 +00:00
|
|
|
for(int i = 0; i < _countof(m_texture); ++i)
|
|
|
|
m_texture[i] = NULL;
|
|
|
|
|
|
|
|
m_dup = NULL;
|
|
|
|
m_output = NULL;
|
|
|
|
m_deviceContext = NULL;
|
|
|
|
m_device = NULL;
|
|
|
|
m_dxgiFactory = NULL;
|
2017-12-29 09:53:52 +00:00
|
|
|
|
2017-11-02 06:55:25 +00:00
|
|
|
m_initialized = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FrameType DXGI::GetFrameType()
|
|
|
|
{
|
|
|
|
if (!m_initialized)
|
|
|
|
return FRAME_TYPE_INVALID;
|
2017-12-29 09:53:52 +00:00
|
|
|
return m_frameType;
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t DXGI::GetMaxFrameSize()
|
|
|
|
{
|
2017-11-03 11:20:48 +00:00
|
|
|
if (!m_initialized)
|
2017-11-02 11:37:19 +00:00
|
|
|
return 0;
|
|
|
|
|
2017-11-17 04:24:01 +00:00
|
|
|
return (m_width * m_height * 4);
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 06:48:44 +00:00
|
|
|
unsigned int Capture::DXGI::Capture()
|
2017-11-02 06:55:25 +00:00
|
|
|
{
|
2017-11-03 17:00:00 +00:00
|
|
|
if (!m_initialized)
|
2017-12-07 19:24:17 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
2017-11-03 17:00:00 +00:00
|
|
|
|
2018-10-03 14:07:34 +00:00
|
|
|
CursorInfo cursor = { 0 };
|
2017-11-03 11:20:48 +00:00
|
|
|
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
2017-12-14 19:18:31 +00:00
|
|
|
IDXGIResourcePtr res;
|
2018-10-09 06:48:44 +00:00
|
|
|
unsigned int ret;
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-11-03 11:20:48 +00:00
|
|
|
HRESULT status;
|
2018-10-04 07:05:08 +00:00
|
|
|
for (int retryCount = 0; retryCount < 2; ++retryCount)
|
2017-11-02 11:37:19 +00:00
|
|
|
{
|
2018-10-09 06:48:44 +00:00
|
|
|
ret = ReleaseFrame();
|
2018-10-04 07:05:08 +00:00
|
|
|
if (ret != GRAB_STATUS_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
status = m_dup->AcquireNextFrame(1000, &frameInfo, &res);
|
|
|
|
|
|
|
|
switch (status)
|
2017-12-11 20:56:50 +00:00
|
|
|
{
|
2018-10-04 07:05:08 +00:00
|
|
|
case S_OK:
|
|
|
|
m_releaseFrame = true;
|
|
|
|
break;
|
2018-09-27 02:49:52 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
case DXGI_ERROR_WAIT_TIMEOUT:
|
2018-09-29 17:50:43 +00:00
|
|
|
return GRAB_STATUS_TIMEOUT;
|
2017-12-15 23:24:37 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
// desktop switch, mode change, switch DWM on or off or Secure Desktop
|
|
|
|
case DXGI_ERROR_ACCESS_LOST:
|
|
|
|
case WAIT_ABANDONED:
|
|
|
|
return GRAB_STATUS_REINIT;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// unknown failure
|
|
|
|
DEBUG_WINERROR("AcquireNextFrame failed", status);
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
2017-12-11 20:56:50 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
// if we have a mouse update
|
|
|
|
if (frameInfo.LastMouseUpdateTime.QuadPart)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
m_lastCursorX != frameInfo.PointerPosition.Position.x ||
|
|
|
|
m_lastCursorY != frameInfo.PointerPosition.Position.y
|
|
|
|
) {
|
2018-10-09 06:48:44 +00:00
|
|
|
ret |= GRAB_STATUS_CURSOR;
|
2018-10-04 07:05:08 +00:00
|
|
|
cursor.hasPos = true;
|
|
|
|
cursor.x = m_lastCursorX = frameInfo.PointerPosition.Position.x;
|
|
|
|
cursor.y = m_lastCursorY = frameInfo.PointerPosition.Position.y;
|
|
|
|
}
|
2017-12-18 01:03:22 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
if (m_lastMouseVis != frameInfo.PointerPosition.Visible)
|
2017-12-11 20:56:50 +00:00
|
|
|
{
|
2018-10-09 06:48:44 +00:00
|
|
|
ret |= GRAB_STATUS_CURSOR;
|
2018-10-04 07:05:08 +00:00
|
|
|
m_lastMouseVis = frameInfo.PointerPosition.Visible;
|
|
|
|
}
|
2017-12-12 15:29:53 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
cursor.visible = m_lastMouseVis == TRUE;
|
|
|
|
}
|
2018-10-09 06:48:44 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// always report the mouse position to prevent the guest losing sync (ie: dragging windows)
|
|
|
|
POINT curPos;
|
2018-10-09 07:10:59 +00:00
|
|
|
if (GetCursorPos(&curPos))
|
2018-10-09 06:48:44 +00:00
|
|
|
{
|
2018-10-09 07:10:59 +00:00
|
|
|
curPos.x -= m_hotSpot.x;
|
|
|
|
curPos.y -= m_hotSpot.y;
|
|
|
|
|
|
|
|
if (curPos.x != m_lastCursorX || curPos.y != m_lastCursorY)
|
|
|
|
{
|
|
|
|
ret |= GRAB_STATUS_CURSOR;
|
|
|
|
cursor.hasPos = true;
|
|
|
|
cursor.x = m_lastCursorX = curPos.x;
|
|
|
|
cursor.y = m_lastCursorY = curPos.y;
|
|
|
|
cursor.visible = m_lastMouseVis;
|
|
|
|
}
|
2018-10-09 06:48:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-12 15:29:53 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
// if the pointer shape has changed
|
|
|
|
if (frameInfo.PointerShapeBufferSize > 0)
|
|
|
|
{
|
|
|
|
CursorBuffer * buf;
|
2017-12-11 20:56:50 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
if (m_cursorData.size() == 0)
|
2017-12-11 20:56:50 +00:00
|
|
|
{
|
2018-10-04 07:05:08 +00:00
|
|
|
// create a new buffer
|
|
|
|
buf = new CursorBuffer;
|
|
|
|
buf->buffer = new char[frameInfo.PointerShapeBufferSize];
|
|
|
|
buf->bufferSize = frameInfo.PointerShapeBufferSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// get an existing buffer from the pool
|
|
|
|
EnterCriticalSection(&m_cursorDataCS);
|
|
|
|
buf = m_cursorData.front();
|
|
|
|
m_cursorData.pop_front();
|
|
|
|
LeaveCriticalSection(&m_cursorDataCS);
|
|
|
|
|
|
|
|
// resize the buffer if required
|
|
|
|
if (buf->bufferSize < frameInfo.PointerShapeBufferSize)
|
2018-10-03 14:07:34 +00:00
|
|
|
{
|
2018-10-04 07:05:08 +00:00
|
|
|
delete[] buf->buffer;
|
2018-10-03 14:07:34 +00:00
|
|
|
buf->buffer = new char[frameInfo.PointerShapeBufferSize];
|
|
|
|
buf->bufferSize = frameInfo.PointerShapeBufferSize;
|
|
|
|
}
|
2018-10-04 07:05:08 +00:00
|
|
|
}
|
2017-12-11 20:56:50 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
buf->pointerSize = 0;
|
|
|
|
cursor.shape = buf;
|
2018-10-09 06:48:44 +00:00
|
|
|
ret |= GRAB_STATUS_CURSOR;
|
2018-10-03 14:07:34 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
|
|
|
status = m_dup->GetFramePointerShape(buf->bufferSize, buf->buffer, &buf->pointerSize, &shapeInfo);
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_WINERROR("Failed to get the new pointer shape", status);
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
2017-12-11 20:56:50 +00:00
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
switch (shapeInfo.Type)
|
|
|
|
{
|
2018-10-03 14:07:34 +00:00
|
|
|
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : cursor.type = CURSOR_TYPE_COLOR; break;
|
|
|
|
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: cursor.type = CURSOR_TYPE_MASKED_COLOR; break;
|
|
|
|
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : cursor.type = CURSOR_TYPE_MONOCHROME; break;
|
2017-12-29 09:53:52 +00:00
|
|
|
default:
|
|
|
|
DEBUG_ERROR("Invalid cursor type");
|
|
|
|
return GRAB_STATUS_ERROR;
|
2018-10-03 14:07:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
cursor.w = shapeInfo.Width;
|
|
|
|
cursor.h = shapeInfo.Height;
|
|
|
|
cursor.pitch = shapeInfo.Pitch;
|
2018-10-09 07:10:59 +00:00
|
|
|
m_hotSpot.x = shapeInfo.HotSpot.x;
|
|
|
|
m_hotSpot.y = shapeInfo.HotSpot.y;
|
2018-10-04 07:05:08 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 06:48:44 +00:00
|
|
|
if (ret & GRAB_STATUS_CURSOR)
|
2018-10-04 07:05:08 +00:00
|
|
|
{
|
|
|
|
// push the cursor update into the queue
|
|
|
|
EnterCriticalSection(&m_cursorCS);
|
|
|
|
m_cursorUpdates.push_back(cursor);
|
|
|
|
LeaveCriticalSection(&m_cursorCS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we don't have frame data
|
|
|
|
if (frameInfo.LastPresentTime.QuadPart == 0)
|
|
|
|
{
|
|
|
|
// if there is nothing to update, just start again
|
2018-10-09 06:48:44 +00:00
|
|
|
if (!ret)
|
2018-10-03 14:07:34 +00:00
|
|
|
{
|
2018-10-04 07:05:08 +00:00
|
|
|
--retryCount;
|
|
|
|
continue;
|
2017-12-11 20:56:50 +00:00
|
|
|
}
|
|
|
|
|
2018-07-28 00:25:57 +00:00
|
|
|
res = NULL;
|
2018-10-09 06:48:44 +00:00
|
|
|
ret |= GRAB_STATUS_OK;
|
|
|
|
return ret;
|
2017-12-11 20:56:50 +00:00
|
|
|
}
|
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
// success, break out of the retry loop
|
|
|
|
break;
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 06:48:44 +00:00
|
|
|
ret |= GRAB_STATUS_FRAME;
|
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
// ensure we have a frame
|
|
|
|
if (!m_releaseFrame)
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WINERROR("Failed to acquire next frame", status);
|
2017-12-07 19:24:17 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
2018-10-04 07:05:08 +00:00
|
|
|
// get the texture
|
|
|
|
res.QueryInterface(IID_PPV_ARGS(&m_ftexture));
|
2018-09-29 17:50:43 +00:00
|
|
|
if (!m_ftexture)
|
2017-11-03 11:20:48 +00:00
|
|
|
{
|
|
|
|
DEBUG_ERROR("Failed to get src ID3D11Texture2D");
|
2017-12-07 19:24:17 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
2017-11-03 11:20:48 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 06:48:44 +00:00
|
|
|
ret |= GRAB_STATUS_OK;
|
|
|
|
return ret;
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-05-31 08:51:28 +00:00
|
|
|
GrabStatus Capture::DXGI::ReleaseFrame()
|
|
|
|
{
|
|
|
|
if (!m_releaseFrame)
|
|
|
|
return GRAB_STATUS_OK;
|
|
|
|
|
|
|
|
m_releaseFrame = false;
|
2018-09-29 17:50:43 +00:00
|
|
|
m_ftexture = NULL;
|
|
|
|
|
2018-05-31 08:51:28 +00:00
|
|
|
switch (m_dup->ReleaseFrame())
|
|
|
|
{
|
|
|
|
case S_OK:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DXGI_ERROR_INVALID_CALL:
|
|
|
|
DEBUG_ERROR("Frame was already released");
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
|
|
|
|
case WAIT_ABANDONED:
|
|
|
|
case DXGI_ERROR_ACCESS_LOST:
|
|
|
|
return GRAB_STATUS_REINIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GRAB_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
2018-09-29 17:50:43 +00:00
|
|
|
GrabStatus Capture::DXGI::DiscardFrame()
|
|
|
|
{
|
|
|
|
return ReleaseFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
GrabStatus Capture::DXGI::GrabFrameRaw(FrameInfo & frame)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
GrabStatus result;
|
|
|
|
D3D11_MAPPED_SUBRESOURCE mapping;
|
2017-12-30 13:32:39 +00:00
|
|
|
|
2018-09-29 17:50:43 +00:00
|
|
|
m_deviceContext->CopyResource(m_texture[0], m_ftexture);
|
2018-05-31 14:38:15 +00:00
|
|
|
|
|
|
|
result = ReleaseFrame();
|
|
|
|
if (result != GRAB_STATUS_OK)
|
|
|
|
return result;
|
|
|
|
|
2017-12-29 09:53:52 +00:00
|
|
|
HRESULT status;
|
2018-07-27 20:15:55 +00:00
|
|
|
status = m_deviceContext->Map(m_texture[0], 0, D3D11_MAP_READ, 0, &mapping);
|
2017-12-18 10:34:44 +00:00
|
|
|
if (FAILED(status))
|
|
|
|
{
|
2017-12-29 09:53:52 +00:00
|
|
|
DEBUG_WINERROR("Failed to map the texture", status);
|
2017-12-18 10:34:44 +00:00
|
|
|
DeInitialize();
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
2018-09-26 11:20:17 +00:00
|
|
|
|
|
|
|
frame.pitch = m_width * 4;
|
|
|
|
frame.stride = m_width;
|
2018-10-04 07:05:08 +00:00
|
|
|
|
|
|
|
if (frame.pitch == mapping.RowPitch)
|
|
|
|
memcpySSE(frame.buffer, mapping.pData, frame.pitch * m_height);
|
|
|
|
else
|
|
|
|
for(unsigned int y = 0; y < m_height; ++y)
|
|
|
|
memcpySSE(
|
|
|
|
(uint8_t *)frame.buffer + (frame.pitch * y),
|
|
|
|
(uint8_t *)mapping.pData + (mapping.RowPitch * y),
|
|
|
|
frame.pitch
|
|
|
|
);
|
2017-12-14 19:15:03 +00:00
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
m_deviceContext->Unmap(m_texture[0], 0);
|
2017-11-02 11:37:19 +00:00
|
|
|
|
2017-12-07 19:24:17 +00:00
|
|
|
return GRAB_STATUS_OK;
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 17:50:43 +00:00
|
|
|
GrabStatus Capture::DXGI::GrabFrameYUV420(struct FrameInfo & frame)
|
2018-07-27 16:27:36 +00:00
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
GrabStatus result;
|
2018-07-27 16:27:36 +00:00
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
TextureList planes;
|
2018-09-29 17:50:43 +00:00
|
|
|
if (!m_textureConverter->Convert(m_ftexture, planes))
|
2018-07-27 16:27:36 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
for(int i = 0; i < 3; ++i)
|
2018-07-27 16:27:36 +00:00
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
ID3D11Texture2DPtr t = planes.at(i);
|
2018-07-28 00:25:57 +00:00
|
|
|
m_deviceContext->CopyResource(m_texture[i], t);
|
2018-07-27 16:27:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result = ReleaseFrame();
|
|
|
|
if (result != GRAB_STATUS_OK)
|
|
|
|
return result;
|
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
uint8_t * data = (uint8_t *)frame.buffer;
|
|
|
|
size_t remain = frame.bufferSize;
|
|
|
|
for(int i = 0; i < 3; ++i)
|
2018-07-27 16:27:36 +00:00
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
HRESULT status;
|
|
|
|
D3D11_MAPPED_SUBRESOURCE mapping;
|
2018-07-27 21:23:51 +00:00
|
|
|
D3D11_TEXTURE2D_DESC desc;
|
2018-07-27 20:15:55 +00:00
|
|
|
|
2018-07-27 21:23:51 +00:00
|
|
|
m_texture[i]->GetDesc(&desc);
|
2018-07-27 20:15:55 +00:00
|
|
|
status = m_deviceContext->Map(m_texture[i], 0, D3D11_MAP_READ, 0, &mapping);
|
|
|
|
if (FAILED(status))
|
|
|
|
{
|
|
|
|
DEBUG_WINERROR("Failed to map the texture", status);
|
|
|
|
DeInitialize();
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
2018-07-27 16:27:36 +00:00
|
|
|
|
2018-07-27 21:48:10 +00:00
|
|
|
const unsigned int size = desc.Height * desc.Width;
|
2018-07-27 20:15:55 +00:00
|
|
|
if (size > remain)
|
|
|
|
{
|
|
|
|
m_deviceContext->Unmap(m_texture[i], 0);
|
|
|
|
DEBUG_ERROR("Too much data to fit in buffer");
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
2018-07-27 16:27:36 +00:00
|
|
|
|
2018-07-27 21:48:10 +00:00
|
|
|
const uint8_t * src = (uint8_t *)mapping.pData;
|
|
|
|
for(unsigned int y = 0; y < desc.Height; ++y)
|
|
|
|
{
|
|
|
|
memcpySSE(data, src, desc.Width);
|
|
|
|
data += desc.Width;
|
|
|
|
src += mapping.RowPitch;
|
|
|
|
}
|
2018-07-27 20:15:55 +00:00
|
|
|
m_deviceContext->Unmap(m_texture[i], 0);
|
2018-07-27 21:48:10 +00:00
|
|
|
remain -= size;
|
2018-07-27 20:15:55 +00:00
|
|
|
}
|
2018-07-27 16:27:36 +00:00
|
|
|
|
2018-07-27 20:15:55 +00:00
|
|
|
frame.pitch = m_width;
|
|
|
|
frame.stride = m_width;
|
2018-07-27 16:27:36 +00:00
|
|
|
return GRAB_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
2018-09-29 17:50:43 +00:00
|
|
|
GrabStatus Capture::DXGI::GrabFrameH264(struct FrameInfo & frame)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-07-27 20:15:55 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
#if 0
|
2017-12-29 09:53:52 +00:00
|
|
|
while(true)
|
|
|
|
{
|
2018-07-25 19:50:06 +00:00
|
|
|
unsigned int events = m_h264->Process();
|
|
|
|
if (events & MFT::H264_EVENT_ERROR)
|
|
|
|
return GRAB_STATUS_ERROR;
|
2017-12-29 09:53:52 +00:00
|
|
|
|
2018-07-25 19:50:06 +00:00
|
|
|
if (events & MFT::H264_EVENT_NEEDS_DATA)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
|
|
|
GrabStatus result;
|
2018-07-25 19:50:06 +00:00
|
|
|
ID3D11Texture2DPtr texture;
|
2017-12-30 13:32:39 +00:00
|
|
|
bool timeout;
|
|
|
|
|
2018-07-25 19:50:06 +00:00
|
|
|
result = GrabFrameTexture(frame, cursor, texture, timeout);
|
2018-07-27 16:27:36 +00:00
|
|
|
if (result != GRAB_STATUS_OK)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-07-27 16:27:36 +00:00
|
|
|
ReleaseFrame();
|
|
|
|
continue;
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-27 16:27:36 +00:00
|
|
|
if (!timeout)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-07-27 16:27:36 +00:00
|
|
|
if (!m_textureConverter->Convert(texture))
|
|
|
|
{
|
|
|
|
SafeRelease(&texture);
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 19:50:06 +00:00
|
|
|
if (!m_h264->ProvideFrame(texture))
|
2017-12-29 09:53:52 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
|
2018-05-31 08:51:28 +00:00
|
|
|
ReleaseFrame();
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 19:50:06 +00:00
|
|
|
if (events & MFT::H264_EVENT_HAS_DATA)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-07-25 19:50:06 +00:00
|
|
|
if (!m_h264->GetFrame(frame.buffer, frame.bufferSize, frame.pitch))
|
2017-12-29 09:53:52 +00:00
|
|
|
return GRAB_STATUS_ERROR;
|
2017-12-29 10:01:02 +00:00
|
|
|
|
2017-12-29 09:53:52 +00:00
|
|
|
return GRAB_STATUS_OK;
|
|
|
|
}
|
|
|
|
}
|
2018-07-27 20:15:55 +00:00
|
|
|
#endif
|
2017-12-29 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 17:50:43 +00:00
|
|
|
GrabStatus DXGI::GetFrame(struct FrameInfo & frame)
|
2017-12-29 09:53:52 +00:00
|
|
|
{
|
2018-09-29 17:50:43 +00:00
|
|
|
if (!m_ftexture)
|
|
|
|
{
|
|
|
|
DEBUG_ERROR("A frame has not been captured");
|
|
|
|
return GRAB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
|
2017-12-29 09:53:52 +00:00
|
|
|
frame.width = m_width;
|
|
|
|
frame.height = m_height;
|
|
|
|
|
2018-07-27 16:27:36 +00:00
|
|
|
switch (m_frameType)
|
2018-09-29 17:50:43 +00:00
|
|
|
{
|
|
|
|
case FRAME_TYPE_YUV420: return GrabFrameYUV420(frame);
|
|
|
|
case FRAME_TYPE_H264 : return GrabFrameH264 (frame);
|
2018-07-27 16:27:36 +00:00
|
|
|
}
|
2018-07-27 16:30:02 +00:00
|
|
|
|
2018-09-29 17:50:43 +00:00
|
|
|
return GrabFrameRaw(frame);
|
|
|
|
}
|
|
|
|
|
2018-10-03 14:07:34 +00:00
|
|
|
bool DXGI::GetCursor(CursorInfo & cursor)
|
2018-09-29 17:50:43 +00:00
|
|
|
{
|
2018-10-03 14:07:34 +00:00
|
|
|
if (!m_cursorUpdates.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
EnterCriticalSection(&m_cursorCS);
|
|
|
|
cursor = m_cursorUpdates.front();
|
|
|
|
m_cursorUpdates.pop_front();
|
|
|
|
LeaveCriticalSection(&m_cursorCS);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DXGI::FreeCursor(CursorInfo & cursor)
|
|
|
|
{
|
|
|
|
/* put the buffer back into the pool */
|
|
|
|
if (cursor.shape)
|
|
|
|
{
|
|
|
|
EnterCriticalSection(&m_cursorDataCS);
|
|
|
|
m_cursorData.push_front(cursor.shape);
|
|
|
|
LeaveCriticalSection(&m_cursorDataCS);
|
|
|
|
}
|
2017-11-02 06:55:25 +00:00
|
|
|
}
|