{"id":250,"date":"2026-06-12T13:30:05","date_gmt":"2026-06-12T12:30:05","guid":{"rendered":"https:\/\/myjetsonnano.perseus314.com\/?p=250"},"modified":"2026-06-16T00:22:09","modified_gmt":"2026-06-15T23:22:09","slug":"networkcamera","status":"publish","type":"post","link":"https:\/\/myjetsonnano.perseus314.com\/index.php\/2026\/06\/12\/networkcamera\/","title":{"rendered":"NetworkCamera"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code>#pragma once\n\n#include &lt;iostream>\n#include &lt;string>\n#include &lt;thread>\n#include &lt;mutex>\n#include &lt;vector>\n#include &lt;winsock2.h>\n#include &lt;ws2tcpip.h>\n#include &lt;opencv2\/opencv.hpp>\n\n\/\/ Ensure Windows socket libraries are linked\n#pragma comment(lib, \"Ws2_32.lib\")\n\nclass NetworkCamera {\npublic:\n    NetworkCamera();\n    ~NetworkCamera();\n\n    \/\/ Establishes connection to the Raspberry Pi\n    bool connectToDevice(const std::string&amp; ipAddress, int port);\n    \n    \/\/ Disconnects and shuts down background threads cleanly\n    void disconnect();\n\n    \/\/ Call this after your settle delay to get the absolute freshest frame\n    cv::Mat getLatestFrame();\n\n    bool isConnected() const { return m_connected; }\n\nprivate:\n    \/\/ Background thread function that continuously drains the socket network buffer\n    void captureLoop();\n\n    \/\/ Network handles\n    SOCKET m_connectSocket;\n    bool m_connected;\n    bool m_running;\n\n    \/\/ Background streaming thread\n    std::thread m_captureThread;\n\n    \/\/ Double-buffering architecture\n    cv::Mat m_bufferA;\n    cv::Mat m_bufferB;\n    cv::Mat* m_writePtr; \/\/ Where the network thread decodes the incoming image\n    cv::Mat* m_readPtr;  \/\/ Where the main loop safely reads from\n    \n    std::mutex m_bufferMutex;\n};<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>#include \"NetworkCamera.h\"\n\nNetworkCamera::NetworkCamera() \n    : m_connectSocket(INVALID_SOCKET), m_connected(false), m_running(false) {\n    \/\/ Point our buffers to our local matrices\n    m_writePtr = &amp;m_bufferA;\n    m_readPtr = &amp;m_bufferB;\n}\n\nNetworkCamera::~NetworkCamera() {\n    disconnect();\n}\n\nbool NetworkCamera::connectToDevice(const std::string&amp; ipAddress, int port) {\n    \/\/ Initialize Winsock\n    WSADATA wsaData;\n    if (WSAStartup(MAKEWORD(2, 2), &amp;wsaData) != 0) {\n        std::cerr &lt;&lt; \"Winsock initialization failed.\\n\";\n        return false;\n    }\n\n    \/\/ Create Socket\n    m_connectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    if (m_connectSocket == INVALID_SOCKET) {\n        std::cerr &lt;&lt; \"Socket creation failed.\\n\";\n        WSACleanup();\n        return false;\n    }\n\n    \/\/ Setup hint structure\n    sockaddr_in clientService;\n    clientService.sin_family = AF_INET;\n    clientService.sin_port = htons(port);\n    inet_pton(AF_INET, ipAddress.c_str(), &amp;clientService.sin_addr);\n\n    \/\/ Connect to Raspberry Pi\n    if (connect(m_connectSocket, (SOCKADDR*)&amp;clientService, sizeof(clientService)) == SOCKET_ERROR) {\n        std::cerr &lt;&lt; \"Failed to connect to Pi camera server.\\n\";\n        closesocket(m_connectSocket);\n        WSACleanup();\n        return false;\n    }\n\n    m_connected = true;\n    m_running = true;\n\n    \/\/ Spin up the background network thread\n    m_captureThread = std::thread(&amp;NetworkCamera::captureLoop, this);\n    return true;\n}\n\nvoid NetworkCamera::disconnect() {\n    if (m_running) {\n        m_running = false;\n        \n        \/\/ Close socket to unblock any pending recv() calls in the thread\n        if (m_connectSocket != INVALID_SOCKET) {\n            closesocket(m_connectSocket);\n            m_connectSocket = INVALID_SOCKET;\n        }\n\n        if (m_captureThread.joinable()) {\n            m_captureThread.join();\n        }\n        \n        WSACleanup();\n        m_connected = false;\n    }\n}\n\nvoid NetworkCamera::captureLoop() {\n    while (m_running) {\n        uint32_t imageSize = 0;\n\n        \/\/ 1. Read the 4-byte header telling us how big the upcoming image is\n        int result = recv(m_connectSocket, reinterpret_cast&lt;char*>(&amp;imageSize), sizeof(imageSize), 0);\n        if (result &lt;= 0 || !m_running) break; \n\n        \/\/ Convert from network byte order to host byte order (just in case)\n        imageSize = ntohl(imageSize); \n\n        \/\/ 2. Read the actual image payload bytes (looping until full size reached)\n        std::vector&lt;uchar> byteBuffer(imageSize);\n        int totalBytesRead = 0;\n        \n        while (totalBytesRead &lt; static_cast&lt;int>(imageSize) &amp;&amp; m_running) {\n            int bytesLeft = imageSize - totalBytesRead;\n            int received = recv(m_connectSocket, reinterpret_cast&lt;char*>(byteBuffer.data() + totalBytesRead), bytesLeft, 0);\n            if (received &lt;= 0) break;\n            totalBytesRead += received;\n        }\n\n        if (totalBytesRead &lt; static_cast&lt;int>(imageSize)) continue; \/\/ Fragmented or broken frame\n\n        \/\/ 3. Decode the compressed JPEG bytes straight into our current \"Write\" buffer\n        \/\/ This takes the heavy CPU compression-lifting out of your main logic thread.\n        cv::imdecode(byteBuffer, cv::IMREAD_UNCHANGED, m_writePtr);\n\n        if (!m_writePtr->empty()) {\n            \/\/ 4. Instantly swap pointers. The new image becomes readable, \n            \/\/ and the old image memory space is recycled for the next incoming data.\n            std::lock_guard&lt;std::mutex> lock(m_bufferMutex);\n            std::swap(m_writePtr, m_readPtr);\n        }\n    }\n}\n\ncv::Mat NetworkCamera::getLatestFrame() {\n    std::lock_guard&lt;std::mutex> lock(m_bufferMutex);\n    \/\/ Returns a fast, shallow-copy header of the matrix. \n    \/\/ It is fully isolated and thread-safe because the capture loop will \n    \/\/ write to the *other* buffer pointer on its next iteration.\n    return *m_readPtr;\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":245,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,24,7,23],"tags":[],"class_list":["post-250","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c","category-camera","category-hardware","category-raspberrypi"],"_links":{"self":[{"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/posts\/250","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/comments?post=250"}],"version-history":[{"count":2,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/posts\/250\/revisions"}],"predecessor-version":[{"id":267,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/posts\/250\/revisions\/267"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/media\/245"}],"wp:attachment":[{"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/media?parent=250"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/categories?post=250"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/myjetsonnano.perseus314.com\/index.php\/wp-json\/wp\/v2\/tags?post=250"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}