[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>
@ -29,20 +30,20 @@ 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 width, height; unsigned int maxWidth, maxHeight;
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,15 +148,19 @@ 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");
return false; return false;
} }
params.bWithHWCursor = hwCursor ? TRUE : FALSE; params.bWithHWCursor = hwCursor ? TRUE : FALSE;
params.bDiffMap = useDiffMap ? TRUE : FALSE; params.bEnableSeparateCursorCapture = seperateCursorCapture ? TRUE : FALSE;
params.bDiffMap = useDiffMap ? TRUE : FALSE;
switch(diffMapBlockSize) switch(diffMapBlockSize)
{ {
@ -148,16 +177,26 @@ 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,
const unsigned int width, const unsigned int width,
const unsigned int height, const unsigned int height,
NvFBCFrameGrabInfo * grabInfo NvFBCFrameGrabInfo * grabInfo
) )
{ {
@ -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;
@ -192,5 +248,57 @@ CaptureResult NvFBCToSysCapture(
return CAPTURE_RESULT_ERROR; 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; 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,29 +50,39 @@ 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,
const unsigned int width, const unsigned int width,
const unsigned int height, const unsigned int height,
NvFBCFrameGrabInfo * grabInfo NvFBCFrameGrabInfo * grabInfo
); );
CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer, void * buffer, unsigned int size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif