Created
March 10, 2025 15:25
-
-
Save timleland/4c55cbf55b3d98c019177a4ea36860a1 to your computer and use it in GitHub Desktop.
RTSP Home Dashboard Live Streamer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <opencv2/opencv.hpp> | |
#include <SDL2/SDL.h> | |
#include <thread> | |
#include <atomic> | |
#include <vector> | |
#include <mutex> | |
#include <iostream> | |
std::vector<std::string> rtsp_urls = { | |
"rtsp://URL", | |
"rtsp://URL", | |
"rtsp://URL", | |
"rtsp://URL", | |
}; | |
std::atomic<bool> running(true); | |
int screenWidth = 1920, screenHeight = 1080; | |
struct CameraFeed | |
{ | |
cv::VideoCapture cap; | |
cv::Mat frame; | |
std::atomic<bool> isConnected; | |
std::mutex lock; | |
}; | |
// Camera feeds | |
std::vector<CameraFeed> cameras(4); | |
void captureThread(int index) | |
{ | |
while (running) | |
{ | |
if (!cameras[index].cap.isOpened()) | |
{ | |
std::cout << "[Camera " << index + 1 << "] Connecting...\n"; | |
cameras[index].cap.open(rtsp_urls[index], cv::CAP_FFMPEG); | |
if (!cameras[index].cap.isOpened()) | |
{ | |
std::cout << "[Camera " << index + 1 << "] Connection failed. Retrying in 2 seconds...\n"; | |
std::this_thread::sleep_for(std::chrono::seconds(2)); | |
continue; | |
} | |
cameras[index].cap.set(cv::CAP_PROP_BUFFERSIZE, 1); | |
cameras[index].cap.set(cv::CAP_PROP_FPS, 20); | |
} | |
cv::Mat frame; | |
if (cameras[index].cap.read(frame)) | |
{ | |
std::lock_guard<std::mutex> guard(cameras[index].lock); | |
cameras[index].frame = frame; | |
cameras[index].isConnected = true; | |
} | |
else | |
{ | |
cameras[index].isConnected = false; | |
std::cout << "[Camera " << index + 1 << "] Disconnected. Reconnecting...\n"; | |
cameras[index].cap.release(); | |
std::this_thread::sleep_for(std::chrono::seconds(2)); | |
} | |
} | |
} | |
int main() | |
{ | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) | |
{ | |
std::cerr << "SDL initialization failed! SDL_Error: " << SDL_GetError() << std::endl; | |
return -1; | |
} | |
SDL_DisplayMode dm; | |
if (SDL_GetCurrentDisplayMode(0, &dm) == 0) | |
{ | |
screenWidth = dm.w; | |
screenHeight = dm.h; | |
} | |
else | |
{ | |
std::cerr << "SDL_GetCurrentDisplayMode failed: " << SDL_GetError() << std::endl; | |
} | |
SDL_Window *window = SDL_CreateWindow("RTSP Viewer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, | |
screenWidth, screenHeight, SDL_WINDOW_FULLSCREEN); | |
if (!window) | |
{ | |
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl; | |
return -1; | |
} | |
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); | |
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); | |
int camWidth = screenWidth / 2; | |
int camHeight = screenHeight / 2; | |
std::vector<std::thread> threads; | |
for (int i = 0; i < 4; i++) | |
{ | |
cameras[i].isConnected = false; | |
threads.emplace_back(captureThread, i); | |
} | |
SDL_Event e; | |
bool quit = false; | |
while (!quit) | |
{ | |
while (SDL_PollEvent(&e)) | |
{ | |
if (e.type == SDL_QUIT) | |
quit = true; | |
} | |
SDL_RenderClear(renderer); | |
for (int i = 0; i < 4; i++) | |
{ | |
cv::Mat frame; | |
{ | |
std::lock_guard<std::mutex> guard(cameras[i].lock); | |
if (!cameras[i].frame.empty()) | |
{ | |
frame = cameras[i].frame.clone(); | |
} | |
} | |
if (!frame.empty()) | |
{ | |
// Fix Color: Convert BGR to RGB | |
cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB); | |
cv::resize(frame, frame, cv::Size(camWidth, camHeight)); | |
// Correct pixel format and bit masking | |
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom( | |
frame.data, frame.cols, frame.rows, 24, frame.step, | |
0x000000FF, 0x0000FF00, 0x00FF0000, 0); | |
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); | |
SDL_FreeSurface(surface); | |
SDL_Rect destRect = {(i % 2) * camWidth, (i / 2) * camHeight, camWidth, camHeight}; | |
SDL_RenderCopy(renderer, texture, NULL, &destRect); | |
SDL_DestroyTexture(texture); | |
} | |
} | |
SDL_RenderPresent(renderer); | |
SDL_Delay(10); | |
} | |
running = false; | |
for (auto &t : threads) | |
t.join(); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment