Skip to content

Instantly share code, notes, and snippets.

@datavudeja
Forked from haydenhhyc/#pi_setup.md
Created December 30, 2025 20:19
Show Gist options
  • Select an option

  • Save datavudeja/9ab5fd0577f4fb256a46fc75bb30e633 to your computer and use it in GitHub Desktop.

Select an option

Save datavudeja/9ab5fd0577f4fb256a46fc75bb30e633 to your computer and use it in GitHub Desktop.
Raspberry Pi Media Server Setup

Raspberry Pi Media Server Setup

How to setup Pi to serve RGB and Thermal footage from cameras.

We're using the Logitech webcam c270 and Seek Compat for RGB and Thermal respectively.

1. Setup RTSP Server

Download mediamtx binary from: https://github.com/bluenviron/mediamtx/releases Latest version is v1.8.3 as of writing, change it accordingly.

And extract

wget https://github.com/bluenviron/mediamtx/releases/download/v1.8.3/mediamtx_v1.8.3_linux_amd64.tar.gz
mkdir ./mediamtx
tar -xf mediamtx_v1.8.3_linux_amd64.tar.gz -C ./mediamtx

running the server:

cd ./mediamtx
./mediamtx

2. Setup v4l2loopback

  • Clone from github:
git clone https://github.com/umlaeute/v4l2loopback
  • switch to latest release tag: v0.12.7
  • build and install module
make && sudo make install
sudo depmod -a
  • load mod as root
sudo modprobe v4l2loopback video_nr=2

Will create a loopback device at /dev/video2

  • Change fps of device
cd ./utils/v4l2loopback-ctl set-fps 10 /dev/video2

Will force set 10fps

3. Push Stream from Seek Compact

Install Dependencies:

  • cmake
  • libopencv-dev (>= 2.4)
  • libusb-1.0-0-dev
  • ffmpeg
sudo apt install cmake libopencv-dev libusb-1.0-0-dev ffmpeg
  • clone repo
git clone https://github.com/OpenThermal/libseek-thermal
cd ./libseek-thermal
  • apply dead pixel fix:
    • copy dead_pixel.patch to libseek-thermal root, then apply the patch:
git apply ./dead_pixel.patch
  • build
cd libseek-thermal
mkdir build
cd build
cmake ..
make
  • install
sudo make install
sudo ldconfig
  • download 10-seekthermal.rules file and add udev rule:
# make sure 10-seekthermal.rule in current dir
sudo cp 10-seekthermal.rules /etc/udev/rules.d

sudo udevadm control --reload
  • push thermal stream to v4l2 loopback device
seek_viewer --colormap=11 --rotate=0 --mode=v4l2 --output=/dev/video2
  • push stream to rtsp via ffmpeg
ffmpeg -i /dev/video2 -b:v 2000k -f rtsp rtsp://localhost:8554/thermal
  • -b:v: for setting bitrate
  • url format: rtsp://:8554/
  • endpoint is arbitrary

Push Stream from RGB Camera

  • Find the camera device with v4l2-ctl:
sudo apt install v4l-utils
v4l2-ctl --list-devices
  • Push stream to rtsp server:
ffmpeg -i /dev/videoX -b:v 2000k -f rtsp rtsp://localhost:8554/camera
  • -b:v: for setting bitrate
  • url: rtsp://localhost:8554/, endpoint is arbitrary

Playing Stream with ffplay

Thermal

ffplay -fflags nobuffer -flags low_delay -framedrop rtsp://<host>:8554/thermal

RGB

ffplay -fflags nobuffer -flags low_delay -framedrop rtsp://<host>:8554/camera
  • -fflags nobuffer -flags low_delay -framedrop are flags for streaming with low latency
SUBSYSTEM=="usb", ATTRS{idVendor}=="289d", ATTRS{idProduct}=="0010", GROUP="users", MODE:="0666", SYMLINK+="usb/seekthermal/pir206.$attr{busnum}.$attr{devpath}.$attr{devnum}"
SUBSYSTEM=="usb", ATTRS{idVendor}=="289d", ATTRS{idProduct}=="0011", GROUP="users", MODE:="0666", SYMLINK+="usb/seekthermal/pir324.$attr{busnum}.$attr{devpath}.$attr{devnum}"
ACTION=="add", ATTRS{idVendor}=="289d", ATTR{power/autosuspend}="-1", ATTR{power/control}="on", ATTR{power/level}="on"
diff --git a/src/SeekCam.cpp b/src/SeekCam.cpp
index 82b9552..7617584 100644
--- a/src/SeekCam.cpp
+++ b/src/SeekCam.cpp
@@ -237,7 +237,7 @@ void SeekCam::create_dead_pixel_list(cv::Mat frame, cv::Mat& dead_pixel_mask,
hist.at<float>(0, 0) = 0; /* suppres 0th bin since its usual the highest,
but we don't want this one */
cv::minMaxLoc(hist, nullptr, nullptr, nullptr, &hist_max_value);
- const double threshold = hist_max_value.y - (max_value - hist_max_value.y);
+ const double threshold = hist_max_value.y - (max_value - hist_max_value.y * 0.9);
/* calculate the dead pixels mask */
cv::threshold(tmp, tmp, threshold, 255, cv::THRESH_BINARY);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment