[c-host] nvfbc: continued implementation of NvFBC

This commit is contained in:
Geoffrey McRae 2019-04-10 16:25:13 +10:00
parent 24c99c4ff9
commit 3f13485ced
4 changed files with 200 additions and 99 deletions

View File

@ -36,7 +36,6 @@ typedef enum CaptureFormat
// frame formats
CAPTURE_FMT_BGRA ,
CAPTURE_FMT_RGBA ,
CAPTURE_FMT_ARGB ,
CAPTURE_FMT_RGBA10,
CAPTURE_FMT_YUV420,

View File

@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "interface/capture.h"
#include "interface/platform.h"
#include "debug.h"
#include "windows/windebug.h"
#include <assert.h>
#include <stdlib.h>
#include <windows.h>
@ -29,20 +30,20 @@ Place, Suite 330, Boston, MA 02111-1307 USA
struct iface
{
bool reinit;
NvFBCToSys * nvfbc;
bool reinit;
NvFBCHandle nvfbc;
void * pointerShape;
unsigned int pointerSize;
unsigned int width, height;
unsigned int maxWidth, maxHeight;
unsigned int width , height;
uint8_t * frameBuffer;
uint8_t * diffMap;
NvFBCFrameGrabInfo grabInfo;
osEventHandle * frameEvent;
osEventHandle * pointerEvent;
HANDLE cursorEvent;
};
static struct iface * this = NULL;
@ -75,7 +76,7 @@ static bool nvfbc_create()
this = (struct iface *)calloc(sizeof(struct iface), 1);
if (!NvFBCToSysCreate(NULL, 0, &this->nvfbc))
if (!NvFBCToSysCreate(NULL, 0, &this->nvfbc, &this->maxWidth, &this->maxHeight))
{
nvfbc_free();
return false;
@ -89,14 +90,6 @@ static bool nvfbc_create()
return false;
}
this->pointerEvent = os_createEvent(true);
if (!this->pointerEvent)
{
DEBUG_ERROR("failed to create the pointer event");
nvfbc_free();
return false;
}
return true;
}
@ -108,23 +101,23 @@ static bool nvfbc_init(void * pointerShape, const unsigned int pointerSize)
getDesktopSize(&this->width, &this->height);
os_resetEvent(this->frameEvent);
os_resetEvent(this->pointerEvent);
if (!NvFBCToSysSetup(
this->nvfbc,
BUFFER_FMT_ARGB,
BUFFER_FMT_ARGB10,
false,
true,
true,
DIFFMAP_BLOCKSIZE_128X128,
false,
0,
(void **)&this->frameBuffer,
(void **)&this->diffMap
NULL,
&this->cursorEvent
))
{
return false;
}
Sleep(100);
return true;
}
@ -140,9 +133,6 @@ static void nvfbc_free()
if (this->frameEvent)
os_freeEvent(this->frameEvent);
if (this->pointerEvent)
os_freeEvent(this->pointerEvent);
free(this);
this = NULL;
NvFBCFree();
@ -150,26 +140,12 @@ static void nvfbc_free()
static unsigned int nvfbc_getMaxFrameSize()
{
return this->width * this->height * 4;
return this->maxWidth * this->maxHeight * 4;
}
static CaptureResult nvfbc_capture()
{
// check if the resolution has changed, if it has we need to re-init to avoid capturing
// black areas as NvFBC doesn't tell us about the change.
unsigned int width, height;
getDesktopSize(&width, &height);
if (this->width != width || this->height != height)
{
DEBUG_INFO("Resolution change detected");
this->reinit = true;
os_signalEvent(this->frameEvent );
os_signalEvent(this->pointerEvent);
return CAPTURE_RESULT_REINIT;
}
getDesktopSize(&this->width, &this->height);
NvFBCFrameGrabInfo grabInfo;
CaptureResult result = NvFBCToSysCapture(
this->nvfbc,
@ -183,23 +159,6 @@ static CaptureResult nvfbc_capture()
if (result != CAPTURE_RESULT_OK)
return result;
// NvFBC doesn't tell us when a timeout occurs, so check the diff map
// to see if anything actually changed
const int dw = (grabInfo.dwWidth + 0x7F) >> 7;
const int dh = (grabInfo.dwHeight + 0x7F) >> 7;
bool diff = false;
for(int y = 0; y < dh && !diff; ++y)
for(int x = 0; x < dw; ++x)
if (this->diffMap[y * dw + x])
{
diff = true;
break;
}
if (!diff)
return CAPTURE_RESULT_TIMEOUT;
memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo));
os_signalEvent(this->frameEvent);
return CAPTURE_RESULT_OK;
@ -220,24 +179,53 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
frame->height = this->grabInfo.dwHeight;
frame->pitch = this->grabInfo.dwBufferWidth * 4;
frame->stride = this->grabInfo.dwBufferWidth;
frame->format = CAPTURE_FMT_BGRA;
// the bIsHDR check isn't reliable, if it's not set check a few pixels to see if
// the alpha channel has data in it. If not HDR the alpha channel should read zeros
this->grabInfo.bIsHDR =
this->grabInfo.bIsHDR ||
(this->frameBuffer[3] != 0) || // top left
(this->frameBuffer[(((frame->height * frame->stride) / 2) + frame->width / 2) * 4 + 3] != 0) || // center
(this->frameBuffer[(((frame->height - 1) * frame->stride) + frame->width - 1) * 4 + 3] != 0); // bottom right
frame->format = this->grabInfo.bIsHDR ? CAPTURE_FMT_RGBA10 : CAPTURE_FMT_BGRA;
memcpy(frame->data, this->frameBuffer, frame->pitch * frame->height);
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_getPointer(CapturePointer * pointer)
{
if (!os_waitEvent(this->pointerEvent, TIMEOUT_INFINITE))
while(true)
{
DEBUG_ERROR("Failed to wait on the pointer event");
bool sig = false;
switch(WaitForSingleObject((HANDLE)this->cursorEvent, INFINITE))
{
case WAIT_OBJECT_0:
sig = true;
break;
case WAIT_ABANDONED:
continue;
case WAIT_TIMEOUT:
continue;
case WAIT_FAILED:
DEBUG_WINERROR("Wait for cursor event failed", GetLastError());
return CAPTURE_RESULT_ERROR;
}
if (sig)
break;
DEBUG_ERROR("Unknown wait event return code");
return CAPTURE_RESULT_ERROR;
}
if (this->reinit)
return CAPTURE_RESULT_REINIT;
return CAPTURE_RESULT_ERROR;
return NvFBCToSysGetCursor(this->nvfbc, pointer, this->pointerShape, this->pointerSize);
}
struct CaptureInterface Capture_NVFBC =

View File

@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "debug.h"
#include "windows/windebug.h"
#include <windows.h>
#include <NvFBC/nvFBCToSys.h>
#ifdef _WIN64
#define NVFBC_DLL "NvFBC64.dll"
@ -39,6 +40,13 @@ struct NVAPI
NvFBC_EnableFunctionType enable;
};
struct stNvFBCHandle
{
NvFBCToSys * nvfbc;
HANDLE cursorEvent;
int retry;
};
static NVAPI nvapi;
bool NvFBCInit()
@ -71,7 +79,13 @@ void NvFBCFree()
nvapi.initialized = false;
}
bool NvFBCToSysCreate(void * privData, unsigned int privDataSize, NvFBCToSys ** nvfbc)
bool NvFBCToSysCreate(
void * privData,
unsigned int privDataSize,
NvFBCHandle * handle,
unsigned int * maxWidth,
unsigned int * maxHeight
)
{
NvFBCCreateParams params = {0};
@ -85,31 +99,42 @@ bool NvFBCToSysCreate(void * privData, unsigned int privDataSize, NvFBCToSys **
if (nvapi.createEx(&params) != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to create an instance of NvFBCToSys");
*nvfbc = NULL;
*handle = NULL;
return false;
}
*nvfbc = static_cast<NvFBCToSys *>(params.pNvFBC);
*handle = (NvFBCHandle)calloc(sizeof(struct stNvFBCHandle), 1);
(*handle)->nvfbc = static_cast<NvFBCToSys *>(params.pNvFBC);
if (maxWidth)
*maxWidth = params.dwMaxDisplayWidth;
if (maxHeight)
*maxHeight = params.dwMaxDisplayHeight;
return true;
}
void NvFBCToSysRelease(NvFBCToSys ** nvfbc)
void NvFBCToSysRelease(NvFBCHandle * handle)
{
if (!*nvfbc)
if (!*handle)
return;
(*nvfbc)->NvFBCToSysRelease();
(*nvfbc) = NULL;
(*handle)->nvfbc->NvFBCToSysRelease();
free(*handle);
*handle = NULL;
}
bool NvFBCToSysSetup(
NvFBCToSys * nvfbc,
NvFBCHandle handle,
enum BufferFormat format,
bool hwCursor,
bool seperateCursorCapture,
bool useDiffMap,
enum DiffMapBlockSize diffMapBlockSize,
void ** frameBuffer,
void ** diffMap
void ** frameBuffer,
void ** diffMap,
HANDLE * cursorEvent
)
{
NVFBC_TOSYS_SETUP_PARAMS params = {0};
@ -123,15 +148,19 @@ bool NvFBCToSysSetup(
case BUFFER_FMT_RGB_PLANAR: params.eMode = NVFBC_TOSYS_RGB_PLANAR; break;
case BUFFER_FMT_XOR : params.eMode = NVFBC_TOSYS_XOR ; break;
case BUFFER_FMT_YUV444p : params.eMode = NVFBC_TOSYS_YUV444p ; break;
case BUFFER_FMT_ARGB10 : params.eMode = NVFBC_TOSYS_ARGB10 ; break;
case BUFFER_FMT_ARGB10 :
params.eMode = NVFBC_TOSYS_ARGB10;
params.bHDRRequest = TRUE;
break;
default:
DEBUG_INFO("Invalid format");
return false;
}
params.bWithHWCursor = hwCursor ? TRUE : FALSE;
params.bDiffMap = useDiffMap ? TRUE : FALSE;
params.bWithHWCursor = hwCursor ? TRUE : FALSE;
params.bEnableSeparateCursorCapture = seperateCursorCapture ? TRUE : FALSE;
params.bDiffMap = useDiffMap ? TRUE : FALSE;
switch(diffMapBlockSize)
{
@ -148,16 +177,26 @@ bool NvFBCToSysSetup(
params.ppBuffer = frameBuffer;
params.ppDiffMap = diffMap;
return nvfbc->NvFBCToSysSetUp(&params) == NVFBC_SUCCESS;
NVFBCRESULT status = handle->nvfbc->NvFBCToSysSetUp(&params);
if (status != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to setup NVFBCToSys");
return false;
}
if (cursorEvent)
*cursorEvent = params.hCursorCaptureEvent;
return true;
}
CaptureResult NvFBCToSysCapture(
NvFBCToSys * nvfbc,
const unsigned int waitTime,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height,
NvFBCHandle handle,
const unsigned int waitTime,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height,
NvFBCFrameGrabInfo * grabInfo
)
{
@ -173,12 +212,29 @@ CaptureResult NvFBCToSysCapture(
params.dwTargetHeight = height;
params.pNvFBCFrameGrabInfo = grabInfo;
NVFBCRESULT status = nvfbc->NvFBCToSysGrabFrame(&params);
grabInfo->bMustRecreate = FALSE;
NVFBCRESULT status = handle->nvfbc->NvFBCToSysGrabFrame(&params);
if (grabInfo->bMustRecreate)
{
DEBUG_INFO("NvFBC reported recreation is required");
return CAPTURE_RESULT_REINIT;
}
switch(status)
{
case NVFBC_SUCCESS:
handle->retry = 0;
break;
case NVFBC_ERROR_INVALID_PARAM:
if (handle->retry < 2)
{
Sleep(100);
++handle->retry;
return CAPTURE_RESULT_TIMEOUT;
}
return CAPTURE_RESULT_ERROR;
case NVFBC_ERROR_DYNAMIC_DISABLE:
DEBUG_ERROR("NvFBC was disabled by someone else");
return CAPTURE_RESULT_ERROR;
@ -192,5 +248,57 @@ CaptureResult NvFBCToSysCapture(
return CAPTURE_RESULT_ERROR;
}
return CAPTURE_RESULT_OK;
}
CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer, void * buffer, unsigned int size)
{
NVFBC_CURSOR_CAPTURE_PARAMS params;
params.dwVersion = NVFBC_CURSOR_CAPTURE_PARAMS_VER;
if (handle->nvfbc->NvFBCToSysCursorCapture(&params) != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to get the cursor");
return CAPTURE_RESULT_ERROR;
}
pointer->x = params.dwXHotSpot;
pointer->y = params.dwYHotSpot;
pointer->width = params.dwWidth;
pointer->height = params.dwHeight;
pointer->pitch = params.dwPitch;
pointer->visible = params.bIsHwCursor;
pointer->shapeUpdate = params.bIsHwCursor;
if (!params.bIsHwCursor)
return CAPTURE_RESULT_OK;
switch(params.dwPointerFlags & 0x7)
{
case 0x1:
pointer->format = CAPTURE_FMT_MONO;
pointer->height *= 2;
break;
case 0x2:
pointer->format = CAPTURE_FMT_COLOR;
break;
case 0x4:
pointer->format = CAPTURE_FMT_MASKED;
break;
default:
DEBUG_ERROR("Invalid/unknown pointer data format");
return CAPTURE_RESULT_ERROR;
}
if (params.dwBufferSize > size)
{
DEBUG_WARN("Cursor data larger then provided buffer");
params.dwBufferSize = size;
}
memcpy(buffer, params.pBits, params.dwBufferSize);
return CAPTURE_RESULT_OK;
}

View File

@ -20,18 +20,14 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include <NvFBC/nvFBC.h>
#ifndef __cplusplus
typedef void * NvFBCToSys;
#else
#include <NvFBC/nvFBCToSys.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include "interface/capture.h"
typedef struct stNvFBCHandle * NvFBCHandle;
enum BufferFormat
{
BUFFER_FMT_ARGB,
@ -54,29 +50,39 @@ enum DiffMapBlockSize
bool NvFBCInit();
void NvFBCFree();
bool NvFBCToSysCreate(void * privData, unsigned int privDataSize, NvFBCToSys ** nvfbc);
void NvFBCToSysRelease(NvFBCToSys ** nvfbc);
bool NvFBCToSysCreate(
void * privData,
unsigned int privDataSize,
NvFBCHandle * handle,
unsigned int * maxWidth,
unsigned int * maxHeight
);
void NvFBCToSysRelease(NvFBCHandle * handle);
bool NvFBCToSysSetup(
NvFBCToSys * nvfbc,
NvFBCHandle handle,
enum BufferFormat format,
bool hwCursor,
bool seperateCursorCapture,
bool useDiffMap,
enum DiffMapBlockSize diffMapBlockSize,
void ** frameBuffer,
void ** diffMap
void ** frameBuffer,
void ** diffMap,
HANDLE * cursorEvent
);
CaptureResult NvFBCToSysCapture(
NvFBCToSys * nvfbc,
const unsigned int waitTime,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height,
NvFBCHandle handle,
const unsigned int waitTime,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height,
NvFBCFrameGrabInfo * grabInfo
);
CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer, void * buffer, unsigned int size);
#ifdef __cplusplus
}
#endif