looking-glass/host/Capture/NvFBC.cpp

303 lines
8 KiB
C++
Raw Normal View History

/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
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-10-31 13:51:53 +00:00
#include "NvFBC.h"
2017-11-02 06:45:25 +00:00
using namespace Capture;
2017-10-31 13:51:53 +00:00
#include <string>
#include "common\debug.h"
2017-11-17 03:10:33 +00:00
#include "common\memcpySSE.h"
2017-10-31 13:51:53 +00:00
#include "Util.h"
#ifdef _WIN64
#define NVFBC_LIBRARY_NAME "NvFBC64.dll"
#else
#define NVFBC_LIBRARY_NAME "NvFBC.dll"
#endif
2017-11-02 06:45:25 +00:00
NvFBC::NvFBC() :
m_options(NULL),
m_optNoCrop(false),
m_optNoWait(false),
2017-11-02 06:45:25 +00:00
m_initialized(false),
m_hDLL(NULL),
m_nvFBC(NULL)
2017-10-31 13:51:53 +00:00
{
2017-11-02 06:45:25 +00:00
}
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
NvFBC::~NvFBC()
{
}
2017-10-31 13:51:53 +00:00
bool NvFBC::Initialize(CaptureOptions * options)
2017-11-02 06:45:25 +00:00
{
if (m_initialized)
DeInitialize();
m_options = options;
m_optNoCrop = false;
for (CaptureOptions::const_iterator it = options->cbegin(); it != options->cend(); ++it)
{
if (_strcmpi(*it, "nocrop") == 0) { m_optNoCrop = true; continue; }
if (_strcmpi(*it, "nowait") == 0) { m_optNoWait = true; continue; }
}
2017-11-02 06:45:25 +00:00
std::string nvfbc = Util::GetSystemRoot() + "\\" + NVFBC_LIBRARY_NAME;
m_hDLL = LoadLibraryA(nvfbc.c_str());
if (!m_hDLL)
2017-10-31 13:51:53 +00:00
{
2017-11-02 06:45:25 +00:00
DEBUG_ERROR("Failed to load the NvFBC library: %d - %s", GetLastError(), nvfbc.c_str());
return false;
2017-10-31 13:51:53 +00:00
}
2017-11-02 06:45:25 +00:00
m_fnCreateEx = (NvFBC_CreateFunctionExType )GetProcAddress(m_hDLL, "NvFBC_CreateEx" );
m_fnSetGlobalFlags = (NvFBC_SetGlobalFlagsType )GetProcAddress(m_hDLL, "NvFBC_SetGlobalFlags");
m_fnGetStatusEx = (NvFBC_GetStatusExFunctionType)GetProcAddress(m_hDLL, "NvFBC_GetStatusEx" );
m_fnEnable = (NvFBC_EnableFunctionType )GetProcAddress(m_hDLL, "NvFBC_Enable" );
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
if (!m_fnCreateEx || !m_fnSetGlobalFlags || !m_fnGetStatusEx || !m_fnEnable)
{
DEBUG_ERROR("Unable to locate required entry points in %s", nvfbc.c_str());
DeInitialize();
return false;
}
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
NvFBCStatusEx status;
ZeroMemory(&status, sizeof(NvFBCStatusEx));
status.dwVersion = NVFBC_STATUS_VER;
status.dwAdapterIdx = 0;
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
NVFBCRESULT ret = m_fnGetStatusEx(&status);
if (ret != NVFBC_SUCCESS)
{
DEBUG_INFO("Attempting to enable NvFBC");
if (m_fnEnable(NVFBC_STATE_ENABLE) == NVFBC_SUCCESS)
2017-10-31 13:51:53 +00:00
{
2017-11-02 06:45:25 +00:00
DEBUG_INFO("Success, attempting to get status again");
ret = m_fnGetStatusEx(&status);
2017-10-31 13:51:53 +00:00
}
if (ret != NVFBC_SUCCESS)
2017-10-31 13:51:53 +00:00
{
2017-11-02 06:45:25 +00:00
DEBUG_ERROR("Failed to get NvFBC status");
2017-10-31 13:51:53 +00:00
DeInitialize();
return false;
}
2017-11-02 06:45:25 +00:00
}
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
if (!status.bIsCapturePossible)
{
DEBUG_ERROR("Capture is not possible, unsupported device or driver");
DeInitialize();
return false;
}
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
if (!status.bCanCreateNow)
{
DEBUG_ERROR("Can not create an instance of NvFBC at this time");
DeInitialize();
return false;
}
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
NvFBCCreateParams params;
ZeroMemory(&params, sizeof(NvFBCCreateParams));
params.dwVersion = NVFBC_CREATE_PARAMS_VER;
params.dwInterfaceType = NVFBC_TO_SYS;
params.pDevice = NULL;
params.dwAdapterIdx = 0;
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
if (m_fnCreateEx(&params) != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to create an instance of NvFBC");
DeInitialize();
return false;
}
2017-11-02 06:45:25 +00:00
m_maxCaptureWidth = params.dwMaxDisplayWidth;
m_maxCaptureHeight = params.dwMaxDisplayHeight;
m_nvFBC = static_cast<NvFBCToSys *>(params.pNvFBC);
2017-11-02 06:45:25 +00:00
NVFBC_TOSYS_SETUP_PARAMS setupParams;
ZeroMemory(&setupParams, sizeof(NVFBC_TOSYS_SETUP_PARAMS));
setupParams.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER;
setupParams.eMode = NVFBC_TOSYS_ARGB;
2017-11-02 06:45:25 +00:00
setupParams.bWithHWCursor = TRUE;
setupParams.bDiffMap = TRUE;
setupParams.eDiffMapBlockSize = (NvU32)NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_128X128;
2017-11-02 06:45:25 +00:00
setupParams.ppBuffer = (void **)&m_frameBuffer;
setupParams.ppDiffMap = (void **)&m_diffMap;
2017-11-02 06:45:25 +00:00
if (m_nvFBC->NvFBCToSysSetUp(&setupParams) != NVFBC_SUCCESS)
{
DEBUG_ERROR("NvFBCToSysSetUp Failed");
DeInitialize();
return false;
}
2017-11-02 06:45:25 +00:00
// this is required according to NVidia sample code
Sleep(100);
2017-11-02 06:45:25 +00:00
ZeroMemory(&m_grabFrameParams, sizeof(NVFBC_TOSYS_GRAB_FRAME_PARAMS));
ZeroMemory(&m_grabInfo, sizeof(NvFBCFrameGrabInfo));
m_grabFrameParams.dwVersion = NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER;
m_grabFrameParams.dwFlags = m_optNoWait ? NVFBC_TOSYS_NOWAIT : NVFBC_TOSYS_WAIT_WITH_TIMEOUT;
m_grabFrameParams.dwWaitTime = 1000;
m_grabFrameParams.eGMode = NVFBC_TOSYS_SOURCEMODE_FULL;
2017-11-02 06:45:25 +00:00
m_grabFrameParams.dwStartX = 0;
m_grabFrameParams.dwStartY = 0;
m_grabFrameParams.dwTargetWidth = 0;
m_grabFrameParams.dwTargetHeight = 0;
2017-11-02 06:45:25 +00:00
m_grabFrameParams.pNvFBCFrameGrabInfo = &m_grabInfo;
2017-11-02 06:45:25 +00:00
m_initialized = true;
return true;
}
2017-11-02 06:45:25 +00:00
void NvFBC::DeInitialize()
{
m_frameBuffer = NULL;
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
if (m_nvFBC)
{
m_nvFBC->NvFBCToSysRelease();
m_nvFBC = NULL;
}
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
m_maxCaptureWidth = 0;
m_maxCaptureHeight = 0;
m_fnCreateEx = NULL;
m_fnSetGlobalFlags = NULL;
m_fnGetStatusEx = NULL;
m_fnEnable = NULL;
2017-10-31 13:51:53 +00:00
2017-11-02 06:45:25 +00:00
if (m_hDLL)
{
FreeLibrary(m_hDLL);
m_hDLL = NULL;
2017-10-31 13:51:53 +00:00
}
2017-11-02 06:45:25 +00:00
m_initialized = false;
}
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
FrameType NvFBC::GetFrameType()
{
if (!m_initialized)
return FRAME_TYPE_INVALID;
2017-10-31 13:51:53 +00:00
return FRAME_TYPE_ARGB;
2017-11-02 06:45:25 +00:00
}
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
size_t NvFBC::GetMaxFrameSize()
{
if (!m_initialized)
return false;
2017-10-31 13:51:53 +00:00
return m_maxCaptureWidth * m_maxCaptureHeight * 4;
2017-11-02 06:45:25 +00:00
}
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
bool NvFBC::GrabFrame(struct FrameInfo & frame)
{
if (!m_initialized)
return false;
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
const HWND hDesktop = GetDesktopWindow();
RECT desktop;
GetWindowRect(hDesktop, &desktop);
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
for(int i = 0; i < 2; ++i)
{
NVFBCRESULT status = m_nvFBC->NvFBCToSysGrabFrame(&m_grabFrameParams);
2017-11-02 06:45:25 +00:00
if (status == NVFBC_SUCCESS)
{
const int diffW = (m_grabInfo.dwWidth + 0x7F) >> 7;
const int diffH = (m_grabInfo.dwHeight + 0x7F) >> 7;
bool hasDiff = false;
for(int y = 0; y < diffH && !hasDiff; ++y)
for (int x = 0; x < diffW; ++x)
if (m_diffMap[y * diffW + x])
{
hasDiff = true;
break;
}
if (!hasDiff)
{
i = 0;
continue;
}
unsigned int dataWidth;
unsigned int dataOffset;
if (m_optNoCrop)
{
dataWidth = m_grabInfo.dwWidth * 4;
dataOffset = 0;
frame.width = m_grabInfo.dwWidth;
frame.height = m_grabInfo.dwHeight;
}
else
{
const unsigned int realHeight = min(m_grabInfo.dwHeight, (unsigned int)(desktop.bottom - desktop.top));
const unsigned int realWidth = min(m_grabInfo.dwWidth , (unsigned int)(desktop.right - desktop.left));
dataWidth = realWidth * 4;
dataOffset =
(((m_grabInfo.dwHeight - realHeight) >> 1) * m_grabInfo.dwBufferWidth +
((m_grabInfo.dwWidth - realWidth ) >> 1)) * 4;
frame.width = realWidth;
frame.height = realHeight;
}
frame.stride = frame.width;
frame.outSize = frame.width * frame.height * 4;
uint8_t *src = (uint8_t *)m_frameBuffer + dataOffset;
uint8_t *dst = (uint8_t *)frame.buffer;
for(unsigned int y = 0; y < frame.height; ++y, dst += dataWidth, src += m_grabInfo.dwBufferWidth * 4)
2017-11-17 03:10:33 +00:00
memcpySSE(dst, src, dataWidth);
2017-11-02 06:45:25 +00:00
return true;
}
if (status == NVFBC_ERROR_DYNAMIC_DISABLE)
{
DEBUG_ERROR("NvFBC was disabled by someone else");
return false;
}
2017-10-31 14:46:47 +00:00
2017-11-02 06:45:25 +00:00
if (status == NVFBC_ERROR_INVALIDATED_SESSION)
{
DEBUG_WARN("Session was invalidated, attempting to restart");
DeInitialize();
if (!Initialize(m_options))
2017-10-31 14:46:47 +00:00
{
2017-11-02 06:45:25 +00:00
DEBUG_ERROR("Failed to re-iniaialize");
return false;
2017-10-31 14:46:47 +00:00
}
}
2017-10-31 13:51:53 +00:00
}
2017-11-02 06:45:25 +00:00
DEBUG_ERROR("Failed to grab frame");
return false;
}