From b8d348c37c54df7acc4c4007ab5a187778ea7ccb Mon Sep 17 00:00:00 2001 From: mgiesen Date: Tue, 12 Dec 2023 13:49:53 +0100 Subject: [PATCH] Adding example how to use a multi threading attempt --- examples/multithreading/example.cpp | 51 ++++++ .../multiThreadingJpgStreamer.hpp | 149 ++++++++++++++++++ examples/{ => simple}/CMakeLists.txt | 0 examples/{ => simple}/example.cpp | 0 examples/{ => simple}/index.html | 0 5 files changed, 200 insertions(+) create mode 100644 examples/multithreading/example.cpp create mode 100644 examples/multithreading/multiThreadingJpgStreamer.hpp rename examples/{ => simple}/CMakeLists.txt (100%) rename examples/{ => simple}/example.cpp (100%) rename examples/{ => simple}/index.html (100%) diff --git a/examples/multithreading/example.cpp b/examples/multithreading/example.cpp new file mode 100644 index 0000000..586b7fd --- /dev/null +++ b/examples/multithreading/example.cpp @@ -0,0 +1,51 @@ +#include +#include +#include "multiThreadingJpgStreamer.h" + +class CameraSimulator { + public: + CameraSimulator(MULTI_THREADING_JPG_STREAMER::Streamer& streamer) : streamer_(streamer) {} + + void run() { + while (true) { + // Simulate capturing a frame from a mock-up camera + cv::Mat mockupFrame(480, 640, CV_8UC3); + mockupFrame.setTo(cv::Scalar(0, 255, 0)); // Green frame as a mock-up + + // Update the stream with the mock-up frame + int updateResult = streamer_.updateFrame(mockupFrame); + + // Sleep for one second (simulating one frame per second) + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + private: + MULTI_THREADING_JPG_STREAMER::Streamer& streamer_; +}; + +int main() { + // Create an instance of your Streamer class (global) + MULTI_THREADING_JPG_STREAMER::Streamer streamer; + + // Start the streaming on a specified port (e.g., port 8080) + unsigned int port = 8080; + unsigned int framerateThrottling = 30; // Set the desired frame rate + unsigned int jpgQuality = 80; // Set the JPEG quality + + streamer.startStreaming(port, framerateThrottling, jpgQuality); + + // Create a CameraSimulator instance that uses the global streamer + CameraSimulator cameraSimulator(streamer); + + // Create a thread for the CameraSimulator + std::thread cameraThread(&CameraSimulator::run, &cameraSimulator); + + // Wait for the camera thread to finish + cameraThread.join(); + + // Stop the streaming when done + streamer.stopStreaming(); + + return 0; +} diff --git a/examples/multithreading/multiThreadingJpgStreamer.hpp b/examples/multithreading/multiThreadingJpgStreamer.hpp new file mode 100644 index 0000000..390ca89 --- /dev/null +++ b/examples/multithreading/multiThreadingJpgStreamer.hpp @@ -0,0 +1,149 @@ +#pragma once + +// Standard Includes +#include +#include +#include +#include +#include + +// External Includes +#include +#include + +namespace MULTI_THREADING_JPG_STREAMER { +enum updateFrameEvents { + FRAME_UPDATE_REFUSED_STREAMER_NOT_RUNNING = 1, + FRAME_UPDATE_REFUSED_FRAME_IS_EMPTY = 2, + FRAME_UPDATE_AND_DIRECT_PUBLISH_SUCCESSFUL = 3, + FRAME_UPDATE_SUCCESSFUL_NO_DIRECT_PUBLISH = 4 +}; + +class Streamer { + private: + nadjieb::MJPEGStreamer MJPEGStreamer; + + std::mutex frameUpdateMutex; + std::vector encodedFrame; + + int framerateThrottling; + + std::thread streamingThread; + + std::atomic keepStreamerRunning = true; + + std::vector encodingOptions = {cv::IMWRITE_JPEG_QUALITY, 90}; + + void publishFrame() { + try { + std::lock_guard lock(frameUpdateMutex); + MJPEGStreamer.publish("/", std::string(encodedFrame.begin(), encodedFrame.end())); + } catch (const std::exception&) { + stopStreaming(); + } + } + + bool threadIsRunning() { return streamingThread.joinable(); } + + bool streamerIsRunning() { return MJPEGStreamer.isRunning(); } + + void runStreaming() { + while (keepStreamerRunning) { + // Exit execution if MJPEGStreamer is no longer running + if (streamerIsRunning() == false) { + break; + } + + // Publish the frame if throttling is desired + if (framerateThrottling > 0) { + publishFrame(); + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / framerateThrottling)); + } + } + + // Stop the streamer + if (MJPEGStreamer.isRunning()) { + MJPEGStreamer.stop(); + } + } + + public: + Streamer() {} + + ~Streamer() { stopStreaming(); } + + /** + * Checks if the streaming thread is active. + * + * @return true if the streaming thread is active, otherwise false. + */ + bool streamingIsRunning() { return threadIsRunning(); } + + /** + * Updates the current frame used for streaming. + * + * @param frame OpenCV Mat representing a new frame. + * + * @throws std::runtime_error if the streaming thread is not running or the streamer is stopped. + */ + int updateFrame(const cv::Mat& frame) { + if (threadIsRunning() == true) { + std::lock_guard lock(frameUpdateMutex); + + if (frame.empty() == true) { + return FRAME_UPDATE_REFUSED_FRAME_IS_EMPTY; + } else { + cv::imencode(".jpg", frame, encodedFrame, encodingOptions); + } + + if (framerateThrottling == 0) { + publishFrame(); + return FRAME_UPDATE_AND_DIRECT_PUBLISH_SUCCESSFUL; + } else { + return FRAME_UPDATE_SUCCESSFUL_NO_DIRECT_PUBLISH; + } + } else { + return FRAME_UPDATE_REFUSED_STREAMER_NOT_RUNNING; + } + } + + /** + * Starts a separate thread in which the streamer is executed. + */ + void startStreaming(unsigned int port, unsigned int framerateThrottling = 0, unsigned int jpgQuality = 90) { + // Check if the streamer is already running and if the parameters have changed + if (threadIsRunning()) { + const bool s1 = this->framerateThrottling != framerateThrottling; + const bool s2 = this->encodingOptions[1] != jpgQuality; + + const bool parameterChanged = s1 || s2; + + if (parameterChanged) { + stopStreaming(); + } else { + return; + } + } + + // Set new parameters and start the streamer + this->framerateThrottling = framerateThrottling; + this->encodingOptions[1] = jpgQuality; + + MJPEGStreamer.start(port); + + streamingThread = std::thread(&Streamer::runStreaming, this); + } + + /** + * Interrupts the execution loop in the streamer thread and performs a join on the thread. + */ + void stopStreaming() { + keepStreamerRunning = false; + + if (streamingThread.joinable()) { + streamingThread.join(); + } + } +}; + +} // namespace MULTI_THREADING_JPG_STREAMER diff --git a/examples/CMakeLists.txt b/examples/simple/CMakeLists.txt similarity index 100% rename from examples/CMakeLists.txt rename to examples/simple/CMakeLists.txt diff --git a/examples/example.cpp b/examples/simple/example.cpp similarity index 100% rename from examples/example.cpp rename to examples/simple/example.cpp diff --git a/examples/index.html b/examples/simple/index.html similarity index 100% rename from examples/index.html rename to examples/simple/index.html