// clang-format off #pragma comment(lib, "ws2_32.lib") #include #include #include #include #include #include #include "Utils.hpp" #include "NvFBCLibrary.h" #include // clang-format on #pragma pack(push, 1) enum class MessageType : u32 { Resize, // tResizeMessage Data, // tDataMessage }; struct tMessageHeader { MessageType type; u32 datalen; // data }; struct tResizeMessage { u32 width; u32 height; }; struct tTile { u32 tile_x; u32 tile_y; u32 tile_width; u32 tile_height; // u32 tile_rgba[tile_width*tile_height] }; struct tDataMessage { u32 tileCount; }; #pragma pack(pop) struct tileRect { u32 x, y, width, height; }; // client for streamserver class cStreamClient { SOCKET tcpSocket { -1 }; public: cStreamClient() = default; ~cStreamClient() { if(tcpSocket != -1) { closesocket(tcpSocket); tcpSocket = -1; } } bool Connect(const char* address, int port) { tcpSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(tcpSocket == -1) { return false; } sockaddr_in clientSvc {}; clientSvc.sin_family = AF_INET; inet_pton(AF_INET, address, &clientSvc.sin_addr.s_addr); // clientSvc.sin_addr.s_addr = inet_addr(address); clientSvc.sin_port = htons(port); if(connect(tcpSocket, (SOCKADDR*)&clientSvc, sizeof(clientSvc)) == SOCKET_ERROR) { printf("No connection socket. Fuck you\n"); return false; } // disable Nagle's alrogithm int nodelay = 1; setsockopt(tcpSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(nodelay)); return true; } void SendResize(const tResizeMessage& resize) { tMessageHeader header; header.type = MessageType::Resize; header.datalen = sizeof(tResizeMessage); send(tcpSocket, (const char*)&header, sizeof(header), 0); send(tcpSocket, (const char*)&resize, sizeof(resize), 0); } void SendData(cSurface& surface, const std::vector& tiles) { tMessageHeader header; header.type = MessageType::Data; // header.datalen = data.get_size() * sizeof(UINT32); header.datalen = sizeof(tDataMessage); // send tile header bool sendFull = false; tDataMessage dm; if(tiles.empty()) { dm.tileCount = 1; sendFull = true; } else { dm.tileCount = (u32)tiles.size(); } // we have writev() at home // writev() at home: send(tcpSocket, (const char*)&header, sizeof(header), 0); send(tcpSocket, (const char*)&dm, sizeof(dm), 0); // send each tile if(!sendFull) { #if 0 std::vector> surfs; for(auto& tile : tiles) { surfs.push_back(surface.ClonePiece(tile)); } for(usize i = 0; i < surfs.size(); ++i) { // things we want! auto& tile_rect = tiles[i]; auto& surf = surfs[i]; //const auto& siz auto* data = surf->Memory(); tTile tile_wire { tile_rect.x, tile_rect.y, tile_rect.width, tile_rect.height }; send(tcpSocket, (const char*)&tile_wire, (int)sizeof(tile_wire), 0); send(tcpSocket, (const char*)&data[0], (int)((tile_rect.width * tile_rect.height) * 4), 0); } #endif std::vector data; for(auto& tile : tiles) { auto* pData = surface.Memory(); tTile tile_wire { tile.x, tile.y, tile.width, tile.height }; data.resize(tile.width * tile.height * 4); for (u32 y = 0; y < tile.height; ++y) { auto* pTileLineStart = &pData[(tile.y + y) * surface.Stride() + tile.x]; memcpy(&data[y * tile.width], pTileLineStart, tile.width * sizeof(u32)); } // send header send(tcpSocket, (const char*)&tile_wire, (int)sizeof(tile_wire), 0); send(tcpSocket, (const char*)&data[0], (int)data.size(), 0); /* send(tcpSocket, (const char*)&tile_wire, (int)sizeof(tile_wire), 0); for(u32 y = 0; y < tile.height; ++y) { auto* pTileLineStart = &pData[(tile.y + y) * surface.Stride() + tile.x]; send(tcpSocket, (const char*)&pTileLineStart[0], tile.width * sizeof(u32), 0); }*/ } #if 0 for(auto& tile : tiles) { tTile tile_wire { tile.x, tile.y, tile.width, tile.height }; std::vector data; data.resize(tile.width * tile.height * 4); for(u32 y = 0; y < tile.height; ++y) { auto* pTileLineStart = &pData[(tile.y + y) * width + tile.x]; memcpy(&data[y * tile.width], pTileLineStart, tile.width * sizeof(u32)); } // send header send(tcpSocket, (const char*)&tile_wire, (int)sizeof(tile_wire), 0); send(tcpSocket, (const char*)&data[0], (int)data.size(), 0); // send data now /*for (auto y = tile.y; y < tile.y + tile.height; ++y) { auto* pTileLineStart = &pData[y * width + tile.x]; send(tcpSocket, (const char*)pTileLineStart, tile.width * sizeof(UINT32), 0); }*/ } #endif } else { auto& size = surface.Size(); tTile tDummyTile { 0, 0, size.width, size.height }; send(tcpSocket, (const char*)&tDummyTile, sizeof(tDummyTile), 0); for(auto y = 0; y < size.height; ++y) { auto* line = &surface.Memory()[y * surface.Stride()]; send(tcpSocket, (const char*)&line[0], (i32)(size.width * sizeof(u32)), 0); } //send(tcpSocket, (const char*)&data.data()[0], (i32)(data.get_size() * sizeof(u32)), 0); } // send(tcpSocket, (const char*)&data.data()[0], data.get_size() * sizeof(UINT32), 0); } }; // FIXME: make this a class, I've clearly got the ability to pull it into one // stuff NvFBCLibrary* nvfbcLibrary = nullptr; NvFBCToSys* nvfbcToSys = nullptr; // filled in by NVFBC unsigned char* frameBuffer = nullptr; unsigned char* diffMap = nullptr; bool nvfbcSetup() { //! Setup the frame grab NVFBC_TOSYS_SETUP_PARAMS fbcSysSetupParams = { 0 }; fbcSysSetupParams.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER; fbcSysSetupParams.eMode = NVFBC_TOSYS_ARGB; fbcSysSetupParams.bWithHWCursor = true; fbcSysSetupParams.ppBuffer = (void**)&frameBuffer; // enable a 32x32 difference map fbcSysSetupParams.bDiffMap = TRUE; fbcSysSetupParams.ppDiffMap = (void**)&diffMap; fbcSysSetupParams.eDiffMapBlockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_32X32; auto status = nvfbcToSys->NvFBCToSysSetUp(&fbcSysSetupParams); return status == NVFBC_SUCCESS; } void nvfbcDestroy() { if(nvfbcToSys) { nvfbcToSys->NvFBCToSysRelease(); diffMap = nullptr; frameBuffer = nullptr; } } bool nvfbcCreate() { if(nvfbcToSys) { nvfbcDestroy(); } DWORD maxDisplayWidth = -1, maxDisplayHeight = -1; //! Create an instance of NvFBCToSys nvfbcToSys = (NvFBCToSys*)nvfbcLibrary->create(NVFBC_TO_SYS, &maxDisplayWidth, &maxDisplayHeight); if(!nvfbcToSys) { return false; } return true; } int main(int argc, char** argv) { WSADATA data; if (WSAStartup(MAKEWORD(2, 2), &data) != NO_ERROR) { return 1; } cStreamClient* client = new cStreamClient(); if (!client->Connect("192.168.1.149", 9438)) { printf("conn failed\n"); return 1; } nvfbcLibrary = new NvFBCLibrary(); //! Load NvFBC if (!nvfbcLibrary->load()) { fprintf(stderr, "Unable to load the NvFBC library\n"); return -1; } if (!nvfbcCreate()) { fprintf(stderr, "Unable to create an instance of NvFBC\n"); return -1; } u32 width = 0; u32 height = 0; bool firstFrame = true; NVFBCRESULT status = NVFBC_SUCCESS; BOOL bRecoveryDone = FALSE; NvFBCFrameGrabInfo grabInfo; //unique_buffer buffer; if (nvfbcSetup()) { // Sleep so that ToSysSetUp forces a framebuffer update Sleep(100); NVFBC_TOSYS_GRAB_FRAME_PARAMS fbcSysGrabParams = { 0 }; std::vector tiles{}; // set up grab parameters fbcSysGrabParams.dwVersion = NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER; fbcSysGrabParams.dwFlags = NVFBC_TOSYS_WAIT_WITH_TIMEOUT; fbcSysGrabParams.dwTargetWidth = 0; fbcSysGrabParams.dwTargetHeight = 0; fbcSysGrabParams.dwStartX = 0; fbcSysGrabParams.dwStartY = 0; fbcSysGrabParams.eGMode = NVFBC_TOSYS_SOURCEMODE_FULL; fbcSysGrabParams.pNvFBCFrameGrabInfo = &grabInfo; fbcSysGrabParams.dwWaitTime = 16; while (true) { // Grab the frame status = nvfbcToSys->NvFBCToSysGrabFrame(&fbcSysGrabParams); if (status == NVFBC_SUCCESS) { bRecoveryDone = FALSE; // handle resizing the buffer if (width != grabInfo.dwWidth || height != grabInfo.dwHeight) { width = grabInfo.dwWidth; height = grabInfo.dwHeight; //buffer.resize(grabInfo.dwWidth * grabInfo.dwHeight); firstFrame = true; client->SendResize(tResizeMessage{ width, height }); } cSurface surf((u32*)frameBuffer, { width, height }, grabInfo.dwBufferWidth); #if 0 // splat the data into an unpadded buffer // REMOVE THIS for(u32 y = 0; y < grabInfo.dwHeight; ++y) { memcpy(&buffer.data()[y * grabInfo.dwWidth], &frameBuffer[(y * grabInfo.dwBufferWidth) * 4], grabInfo.dwWidth * 4); } #endif tiles.clear(); // diffmap if (firstFrame == false) { u32 dwDiffMapWidth = (u32)ceil((f32)width / 32); u32 dwDiffMapHeight = (u32)ceil((f32)height / 32); for (u32 y = 0; y < dwDiffMapHeight; ++y) { for (u32 x = 0; x < dwDiffMapWidth; ++x) { auto& bl = diffMap[y * dwDiffMapWidth + x]; if (bl != 0) { tiles.push_back(tRect{ x * (width / dwDiffMapWidth), // x y * (height / dwDiffMapHeight), // y width / dwDiffMapWidth, // width height / dwDiffMapHeight // height }); } } } // don't send a frame, just wait if (tiles.empty()) continue; } // send that to the server //client->SendData(buffer, width, height, tiles); client->SendData(surf, tiles); if (firstFrame) firstFrame = false; } else { if (bRecoveryDone == TRUE) { fprintf(stderr, "Unable to recover from NvFBC Frame grab failure.\n"); break; } if (status == NVFBC_ERROR_DYNAMIC_DISABLE) { fprintf(stderr, "NvFBC disabled. Quitting\n"); break; } // Try to recover the session if (status == NVFBC_ERROR_INVALIDATED_SESSION) { fprintf(stderr, "Session Invalidated. Attempting recovery\n"); if (!nvfbcCreate()) { fprintf(stderr, "Unable to re-create NvFBC\n"); break; } if (nvfbcSetup()) { bRecoveryDone = TRUE; } else { fprintf(stderr, "Unable to recover from NvFBC Frame grab failure.\n"); break; } } } } } if (status != NVFBC_SUCCESS) { fprintf(stderr, "Unable to setup frame grab.\n"); } nvfbcDestroy(); delete client; delete nvfbcLibrary; return 0; }