Skip to content

Adding example how to use a multi threading attempt #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions examples/multithreading/example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <thread>
#include <vector>
#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;
}
149 changes: 149 additions & 0 deletions examples/multithreading/multiThreadingJpgStreamer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#pragma once

// Standard Includes
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

// External Includes
#include <mjpeg_streamer.hpp>
#include <opencv2/opencv.hpp>

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<uchar> encodedFrame;

int framerateThrottling;

std::thread streamingThread;

std::atomic<bool> keepStreamerRunning = true;

std::vector<int> encodingOptions = {cv::IMWRITE_JPEG_QUALITY, 90};

void publishFrame() {
try {
std::lock_guard<std::mutex> 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<std::mutex> 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
File renamed without changes.
File renamed without changes.
File renamed without changes.