This system streams video from a Raspberry Pi camera to a remote server using RTMP protocol with a buffer for robust transmission over unreliable networks.
Flow:
- Camera (Raspberry Pi): Captures video/audio → Local RTSP streams → Combined stream → RTMP transmission to server
- Server: Receives RTMP stream → YouTube streaming processor
On the camera side, two separate streams get created, one for the video and one for the audio. These then get combined into a single stream and transmitted to the server over RTMP.
- Raspberry Pi 3B+
- Camera Module 3 Wide
- USB Microphone (mono input, converted to stereo)
- Power supply, fan, ...
MediaMTX acts as a robust RTSP server on the Raspberry Pi to manage local streams before RTMP transmission.
wget https://github.com/bluenviron/mediamtx/releases/latest/download/mediamtx_linux_armv7.tar.gz
tar xvf mediamtx_linux_armv7.tar.gz
sudo mv mediamtx /usr/local/bin/Edit your mediamtx.yml:
paths:
video:
source: publisher
audio:
source: publisher
cam:
source: publisherThis allows the various ffmpeg processes to publish to the /video, /audio, and /cam paths.
./mediamtx /path/to/mediamtx.ymlCreate a systemd service file:
sudo nano /etc/systemd/system/mediamtx.servicePaste:
[Unit]
Description=MediaMTX RTSP Server
After=network.target
[Service]
ExecStart=/path/to/mediamtx /path/to/mediamtx.yml
Restart=always
RestartSec=5
User=admin
[Install]
WantedBy=multi-user.targetCreate the configuration directory and file:
sudo mkdir -p /etc/mediamtx
sudo nano /etc/mediamtx/mediamtx.ymlAdd the MediaMTX configuration:
paths:
video:
source: publisher
audio:
source: publisher
cam:
source: publisherEnable and start the service:
sudo systemctl enable mediamtx
sudo systemctl start mediamtxTo automatically start the camera streaming pipeline on boot, create the following systemd service files on the Raspberry Pi.
[Unit]
Description=FFmpeg RTSP Video Stream
After=network.target mediamtx.service
[Service]
ExecStart=/bin/bash -c 'rpicam-vid -t 0 --inline --width 1920 --height 1080 --framerate 30 -o - | ffmpeg -nostdin -thread_queue_size 512 -fflags +genpts -re -i - -c:v copy -an -f rtsp rtsp://localhost:8554/video'
Restart=always
RestartSec=5
User=admin
[Install]
WantedBy=multi-user.targetSave as /etc/systemd/system/ffmpeg-video.service.
[Unit]
Description=FFmpeg RTSP Audio Stream
After=network.target mediamtx.service
[Service]
ExecStart=/bin/bash -c 'ffmpeg -nostdin -f alsa -channels 1 -ar 44100 -i hw:1,0 -af "volume=2.0,pan=stereo|c0=c0|c1=c0" -c:a aac -ar 44100 -b:a 128k -vn -f rtsp rtsp://localhost:8554/audio'
Restart=always
RestartSec=5
User=admin
[Install]
WantedBy=multi-user.targetSave as /etc/systemd/system/ffmpeg-audio.service.
[Unit]
Description=FFmpeg RTSP Combined Stream (Video + Audio)
After=network.target ffmpeg-video.service ffmpeg-audio.service
[Service]
ExecStart=/bin/bash -c 'ffmpeg -i rtsp://localhost:8554/video -i rtsp://localhost:8554/audio -fflags +genpts -avoid_negative_ts make_zero -c:v copy -c:a copy -f rtsp rtsp://localhost:8554/cam'
Restart=always
RestartSec=5
User=admin
[Install]
WantedBy=multi-user.targetSave as /etc/systemd/system/ffmpeg-combined.service.
[Unit]
Description=Stream Transmission to Server
After=network.target ffmpeg-combined.service
[Service]
ExecStart=/bin/bash -c 'ffmpeg -rtsp_transport tcp -i rtsp://localhost:8554/cam -c:v copy -c:a aac -b:a 96k -f flv rtmp://YOUR_SERVER_IP/live/stream
Restart=always
RestartSec=5
User=admin
[Install]
WantedBy=multi-user.targetReplace YOUR_SERVER_IP with your server's IP address. Save as /etc/systemd/system/stream-transmission.service.
sudo systemctl enable mediamtx
sudo systemctl enable ffmpeg-video
sudo systemctl enable ffmpeg-audio
sudo systemctl enable ffmpeg-combined
sudo systemctl enable stream-transmission
sudo systemctl start mediamtx
sudo systemctl start ffmpeg-video
sudo systemctl start ffmpeg-audio
sudo systemctl start ffmpeg-combined
sudo systemctl start stream-transmissionOn the server, receive the stream from the camera and host it locally:
Add the following in /etc/nginx/nginx.conf:
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
record off;
# HLS settings
hls on;
hls_path /tmp/hls;
hls_fragment 10s;
hls_playlist_length 30s;
hls_continuous on;
hls_cleanup on;
}
}
}This makes Nginx listen on port 1935 for RTMP on the /live application. In this case, a 30 second buffer is added for stability.
Restart the service:
sudo systemctl reload nginxLaunch the YouTube streaming processing container (processes local RTSP)
- Place the
youtube-streamfolder on your server. - Launch the Docker container using
docker-composefrom inside theyoutube-streamfolder. - The Python script now connects to the local RTSP stream and relays it to YouTube.
- The webserver in the container serves hold screens when the main stream is not active.
To start the container:
cd youtube-stream
docker-compose build
docker-compose up -d- The Python script (
simple-processor.py) monitors the local RTSP stream atrtsp://localhost:8554/live/stream. - If the RTSP stream is unavailable, the script switches to a hold screen (static image or video).
- You can customize hold screens by replacing files in the
overlaysdirectory insideyoutube-stream. - The Docker Compose file (
docker-compose.yml) defines the service and mounts necessary volumes. - Custom hold screen images and new stream keys can be added dynamically through the webserver (runs on port 3000).
- Logs and status can be checked with:
docker logs stream-processor- To update configuration, edit the files and restart the container:
docker stop stream-processor
docker rm stream-processor
docker-compose build
docker-compose up -d