[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 // frame formats
CAPTURE_FMT_BGRA , CAPTURE_FMT_BGRA ,
CAPTURE_FMT_RGBA , CAPTURE_FMT_RGBA ,
CAPTURE_FMT_ARGB ,
CAPTURE_FMT_RGBA10, CAPTURE_FMT_RGBA10,
CAPTURE_FMT_YUV420, CAPTURE_FMT_YUV420,

View File

@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "interface/capture.h" #include "interface/capture.h"
#include "interface/platform.h" #include "interface/platform.h"
#include "debug.h" #include "debug.h"
#include "windows/windebug.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <windows.h> #include <windows.h>
@ -30,19 +31,19 @@ Place, Suite 330, Boston, MA 02111-1307 USA
struct iface struct iface
{ {
bool reinit; bool reinit;
NvFBCToSys * nvfbc; NvFBCHandle nvfbc;
void * pointerShape; void * pointerShape;
unsigned int pointerSize; unsigned int pointerSize;
unsigned int maxWidth, maxHeight;
unsigned int width , height; unsigned int width , height;
uint8_t * frameBuffer; uint8_t * frameBuffer;
uint8_t * diffMap;
NvFBCFrameGrabInfo grabInfo; NvFBCFrameGrabInfo grabInfo;
osEventHandle * frameEvent; osEventHandle * frameEvent;
osEventHandle * pointerEvent; HANDLE cursorEvent;
}; };
static struct iface * this = NULL; static struct iface * this = NULL;
@ -75,7 +76,7 @@ static bool nvfbc_create()
this = (struct iface *)calloc(sizeof(struct iface), 1); 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(); nvfbc_free();
return false; return false;
@ -89,14 +90,6 @@ static bool nvfbc_create()
return false; return false;
} }
this->pointerEvent = os_createEvent(true);
if (!this->pointerEvent)
{
DEBUG_ERROR("failed to create the pointer event");
nvfbc_free();
return false;
}
return true; return true;
} }
@ -108,23 +101,23 @@ static bool nvfbc_init(void * pointerShape, const unsigned int pointerSize)
getDesktopSize(&this->width, &this->height); getDesktopSize(&this->width, &this->height);
os_resetEvent(this->frameEvent); os_resetEvent(this->frameEvent);
os_resetEvent(this->pointerEvent);
if (!NvFBCToSysSetup( if (!NvFBCToSysSetup(
this->nvfbc, this->nvfbc,
BUFFER_FMT_ARGB, BUFFER_FMT_ARGB10,
false,
true, true,
true, false,
DIFFMAP_BLOCKSIZE_128X128, 0,
(void **)&this->frameBuffer, (void **)&this->frameBuffer,
(void **)&this->diffMap NULL,
&this->cursorEvent
)) ))
{ {
return false; return false;
} }
Sleep(100); Sleep(100);
return true; return true;
} }
@ -140,9 +133,6 @@ static void nvfbc_free()
if (this->frameEvent) if (this->frameEvent)
os_freeEvent(this->frameEvent); os_freeEvent(this->frameEvent);
if (this->pointerEvent)
os_freeEvent(this->pointerEvent);
free(this); free(this);
this = NULL; this = NULL;
NvFBCFree(); NvFBCFree();
@ -150,26 +140,12 @@ static void nvfbc_free()
static unsigned int nvfbc_getMaxFrameSize() static unsigned int nvfbc_getMaxFrameSize()
{ {
return this->width * this->height * 4; return this->maxWidth * this->maxHeight * 4;
} }
static CaptureResult nvfbc_capture() static CaptureResult nvfbc_capture()
{ {
// check if the resolution has changed, if it has we need to re-init to avoid capturing getDesktopSize(&this->width, &this->height);
// 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;
}
NvFBCFrameGrabInfo grabInfo; NvFBCFrameGrabInfo grabInfo;
CaptureResult result = NvFBCToSysCapture( CaptureResult result = NvFBCToSysCapture(
this->nvfbc, this->nvfbc,
@ -183,23 +159,6 @@ static CaptureResult nvfbc_capture()
if (result != CAPTURE_RESULT_OK) if (result != CAPTURE_RESULT_OK)
return result; 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)); memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo));
os_signalEvent(this->frameEvent); os_signalEvent(this->frameEvent);
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
@ -220,24 +179,53 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
frame->height = this->grabInfo.dwHeight; frame->height = this->grabInfo.dwHeight;
frame->pitch = this->grabInfo.dwBufferWidth * 4; frame->pitch = this->grabInfo.dwBufferWidth * 4;
frame->stride = this->grabInfo.dwBufferWidth; 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); memcpy(frame->data, this->frameBuffer, frame->pitch * frame->height);
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
} }
static CaptureResult nvfbc_getPointer(CapturePointer * pointer) 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; return CAPTURE_RESULT_ERROR;
} }
if (this->reinit) if (this->reinit)
return CAPTURE_RESULT_REINIT; return CAPTURE_RESULT_REINIT;
return CAPTURE_RESULT_ERROR; return NvFBCToSysGetCursor(this->nvfbc, pointer, this->pointerShape, this->pointerSize);
} }
struct CaptureInterface Capture_NVFBC = struct CaptureInterface Capture_NVFBC =

View File

@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "debug.h" #include "debug.h"
#include "windows/windebug.h" #include "windows/windebug.h"
#include <windows.h> #include <windows.h>
#include <NvFBC/nvFBCToSys.h>
#ifdef _WIN64 #ifdef _WIN64
#define NVFBC_DLL "NvFBC64.dll" #define NVFBC_DLL "NvFBC64.dll"
@ -39,6 +40,13 @@ struct NVAPI
NvFBC_EnableFunctionType enable; NvFBC_EnableFunctionType enable;
}; };
struct stNvFBCHandle
{
NvFBCToSys * nvfbc;
HANDLE cursorEvent;
int retry;
};
static NVAPI nvapi; static NVAPI nvapi;
bool NvFBCInit() bool NvFBCInit()
@ -71,7 +79,13 @@ void NvFBCFree()
nvapi.initialized = false; 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}; NvFBCCreateParams params = {0};
@ -85,31 +99,42 @@ bool NvFBCToSysCreate(void * privData, unsigned int privDataSize, NvFBCToSys **
if (nvapi.createEx(&params) != NVFBC_SUCCESS) if (nvapi.createEx(&params) != NVFBC_SUCCESS)
{ {
DEBUG_ERROR("Failed to create an instance of NvFBCToSys"); DEBUG_ERROR("Failed to create an instance of NvFBCToSys");
*nvfbc = NULL; *handle = NULL;
return false; 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; return true;
} }
void NvFBCToSysRelease(NvFBCToSys ** nvfbc) void NvFBCToSysRelease(NvFBCHandle * handle)
{ {
if (!*nvfbc) if (!*handle)
return; return;
(*nvfbc)->NvFBCToSysRelease(); (*handle)->nvfbc->NvFBCToSysRelease();
(*nvfbc) = NULL; free(*handle);
*handle = NULL;
} }
bool NvFBCToSysSetup( bool NvFBCToSysSetup(
NvFBCToSys * nvfbc, NvFBCHandle handle,
enum BufferFormat format, enum BufferFormat format,
bool hwCursor, bool hwCursor,
bool seperateCursorCapture,
bool useDiffMap, bool useDiffMap,
enum DiffMapBlockSize diffMapBlockSize, enum DiffMapBlockSize diffMapBlockSize,
void ** frameBuffer, void ** frameBuffer,
void ** diffMap void ** diffMap,
HANDLE * cursorEvent
) )
{ {
NVFBC_TOSYS_SETUP_PARAMS params = {0}; NVFBC_TOSYS_SETUP_PARAMS params = {0};
@ -123,7 +148,10 @@ bool NvFBCToSysSetup(
case BUFFER_FMT_RGB_PLANAR: params.eMode = NVFBC_TOSYS_RGB_PLANAR; break; 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_XOR : params.eMode = NVFBC_TOSYS_XOR ; break;
case BUFFER_FMT_YUV444p : params.eMode = NVFBC_TOSYS_YUV444p ; 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: default:
DEBUG_INFO("Invalid format"); DEBUG_INFO("Invalid format");
@ -131,6 +159,7 @@ bool NvFBCToSysSetup(
} }
params.bWithHWCursor = hwCursor ? TRUE : FALSE; params.bWithHWCursor = hwCursor ? TRUE : FALSE;
params.bEnableSeparateCursorCapture = seperateCursorCapture ? TRUE : FALSE;
params.bDiffMap = useDiffMap ? TRUE : FALSE; params.bDiffMap = useDiffMap ? TRUE : FALSE;
switch(diffMapBlockSize) switch(diffMapBlockSize)
@ -148,11 +177,21 @@ bool NvFBCToSysSetup(
params.ppBuffer = frameBuffer; params.ppBuffer = frameBuffer;
params.ppDiffMap = diffMap; 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( CaptureResult NvFBCToSysCapture(
NvFBCToSys * nvfbc, NvFBCHandle handle,
const unsigned int waitTime, const unsigned int waitTime,
const unsigned int x, const unsigned int x,
const unsigned int y, const unsigned int y,
@ -173,12 +212,29 @@ CaptureResult NvFBCToSysCapture(
params.dwTargetHeight = height; params.dwTargetHeight = height;
params.pNvFBCFrameGrabInfo = grabInfo; 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) switch(status)
{ {
case NVFBC_SUCCESS: case NVFBC_SUCCESS:
handle->retry = 0;
break; 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: case NVFBC_ERROR_DYNAMIC_DISABLE:
DEBUG_ERROR("NvFBC was disabled by someone else"); DEBUG_ERROR("NvFBC was disabled by someone else");
return CAPTURE_RESULT_ERROR; return CAPTURE_RESULT_ERROR;
@ -194,3 +250,55 @@ CaptureResult NvFBCToSysCapture(
return CAPTURE_RESULT_OK; 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 <stdbool.h>
#include <NvFBC/nvFBC.h> #include <NvFBC/nvFBC.h>
#ifndef __cplusplus
typedef void * NvFBCToSys;
#else
#include <NvFBC/nvFBCToSys.h>
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include "interface/capture.h" #include "interface/capture.h"
typedef struct stNvFBCHandle * NvFBCHandle;
enum BufferFormat enum BufferFormat
{ {
BUFFER_FMT_ARGB, BUFFER_FMT_ARGB,
@ -54,21 +50,29 @@ enum DiffMapBlockSize
bool NvFBCInit(); bool NvFBCInit();
void NvFBCFree(); void NvFBCFree();
bool NvFBCToSysCreate(void * privData, unsigned int privDataSize, NvFBCToSys ** nvfbc); bool NvFBCToSysCreate(
void NvFBCToSysRelease(NvFBCToSys ** nvfbc); void * privData,
unsigned int privDataSize,
NvFBCHandle * handle,
unsigned int * maxWidth,
unsigned int * maxHeight
);
void NvFBCToSysRelease(NvFBCHandle * handle);
bool NvFBCToSysSetup( bool NvFBCToSysSetup(
NvFBCToSys * nvfbc, NvFBCHandle handle,
enum BufferFormat format, enum BufferFormat format,
bool hwCursor, bool hwCursor,
bool seperateCursorCapture,
bool useDiffMap, bool useDiffMap,
enum DiffMapBlockSize diffMapBlockSize, enum DiffMapBlockSize diffMapBlockSize,
void ** frameBuffer, void ** frameBuffer,
void ** diffMap void ** diffMap,
HANDLE * cursorEvent
); );
CaptureResult NvFBCToSysCapture( CaptureResult NvFBCToSysCapture(
NvFBCToSys * nvfbc, NvFBCHandle handle,
const unsigned int waitTime, const unsigned int waitTime,
const unsigned int x, const unsigned int x,
const unsigned int y, const unsigned int y,
@ -77,6 +81,8 @@ CaptureResult NvFBCToSysCapture(
NvFBCFrameGrabInfo * grabInfo NvFBCFrameGrabInfo * grabInfo
); );
CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer, void * buffer, unsigned int size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif