#pragma once #include #include #include #include #define UWS_NO_ZLIB #include "App.h" void Logger(const char* format, ...) { time_t now = time(0); tm* ltm = localtime(&now); char buffer[32]; strftime(buffer, 32, "[%H:%M:%S] ", ltm); printf("%s", buffer); va_list args; va_start(args, format); vprintf(format, args); va_end(args); } enum StreamType { ST_ORIGINAL, ST_MASKED, ST_MAX }; struct FrameQueue { std::queue frameQueue; std::mutex frameMutex; }; std::vector frameQueues(ST_MAX); struct FrameHolder { cv::Mat frame; std::mutex frameMutex; }; std::array frameHolders; // Convert cv::Mat to JPEG buffer std::vector matToBytes(const cv::Mat& img) { std::vector buf; cv::imencode(".jpg", img, buf); return buf; } std::vector matToJpeg(const cv::Mat& img) { std::vector buf; cv::imencode(".jpg", img, buf); return buf; } void updateFrame(int type, const cv::Mat& frame) { std::lock_guard lock(frameHolders[type].frameMutex); frameHolders[type].frame = frame.clone(); } cv::Mat compressFrame(const cv::Mat& frame, int quality = 80) { std::vector buf; std::vector params = { cv::IMWRITE_JPEG_QUALITY, quality }; cv::imencode(".jpg", frame, buf, params); return cv::imdecode(buf, cv::IMREAD_COLOR); } void pushToFrameQueue(int type, const cv::Mat& frame) { //cv::Mat compressedFrame = compressFrame(frame, 50); // clear the queue std::queue empty; std::swap(frameQueues[type].frameQueue, empty); frameQueues[type].frameQueue.push(frame.clone()); } struct PerSocketData { }; std::mutex wsMutex; std::vector*> activeWebSockets; void SendFramesToAllClients() { std::lock_guard lock(wsMutex); std::string message; cv::Mat originalFrame, maskedFrame; { std::lock_guard lock(frameQueues[ST_ORIGINAL].frameMutex); if (!frameQueues[ST_ORIGINAL].frameQueue.empty()) { originalFrame = frameQueues[ST_ORIGINAL].frameQueue.front(); frameQueues[ST_ORIGINAL].frameQueue.pop(); } } { std::lock_guard lock(frameQueues[ST_MASKED].frameMutex); if (!frameQueues[ST_MASKED].frameQueue.empty()) { maskedFrame = frameQueues[ST_MASKED].frameQueue.front(); frameQueues[ST_MASKED].frameQueue.pop(); } } if (!originalFrame.empty() && !maskedFrame.empty()) { std::vector originalJpeg = matToJpeg(originalFrame); std::vector maskedJpeg = matToJpeg(maskedFrame); uint32_t originalSize = originalJpeg.size(); uint32_t maskedSize = maskedJpeg.size(); // Prepare the message: originalSize (4 bytes) + maskedSize (4 bytes) + original image data + masked image data message.append(reinterpret_cast(&originalSize), 4); message.append(reinterpret_cast(&maskedSize), 4); message.append(originalJpeg.begin(), originalJpeg.end()); message.append(maskedJpeg.begin(), maskedJpeg.end()); } for (auto ws : activeWebSockets) { ws->send(message, uWS::OpCode::BINARY); } } void RunWebSocketServer(int websocketPort) { auto loop = uWS::Loop::get(); struct us_timer_t* frameTimer = us_create_timer((struct us_loop_t*)loop, 0, 0); us_timer_set(frameTimer, [](struct us_timer_t*) { SendFramesToAllClients(); }, 33, 33); // 33ms interval for approximately 30 FPS uWS::App().ws("/*", { .compression = uWS::SHARED_COMPRESSOR, .maxPayloadLength = 100 * 1024 * 1024, .idleTimeout = 120, .maxBackpressure = 1 * 1024 * 1024, .closeOnBackpressureLimit = false, .resetIdleTimeoutOnSend = false, .sendPingsAutomatically = true, .open = [](auto* ws) { Logger("WebSocket connection opened, current connections: %d\n", activeWebSockets.size() + 1); std::lock_guard lock(wsMutex); activeWebSockets.push_back(ws); Logger("Client address: %s\n", ws->getRemoteAddressAsText().data()); }, .message = [](auto* ws, std::string_view message, uWS::OpCode opCode) { // Handle incoming messages if needed }, .close = [](auto* ws, int code, std::string_view message) { Logger("WebSocket connection closed, current connections: %d\n", activeWebSockets.size() - 1); std::lock_guard lock(wsMutex); activeWebSockets.erase(std::remove(activeWebSockets.begin(), activeWebSockets.end(), ws), activeWebSockets.end()); } }).listen(websocketPort, [websocketPort](auto* listen_socket) { if (listen_socket) { Logger("WebSocket listening on port %i\n", websocketPort); } }).run(); }