FFmpeg + NGINX HTTPS HLS Streaming Server Ubuntu 24.04
1 — Update and install dependencies
sudo apt update
sudo apt install -y ffmpeg nginx python3-yaml openssl
2 — Directory setup
sudo mkdir -p /usr/local/ffmpeg-hls
sudo mkdir -p /var/www/html/cctv
sudo chown -R www-data:www-data /var/www/html
3 — Camera configuration file
Create file /usr/local/ffmpeg-hls/cams.yml
- name: cam1
url: rtsp-url-here
- name: cam2
url: rtsp-url-here
- name: cam3
url: rtsp-url-here
# Add up to cam16 if needed...
4 — Auto FFmpeg launcher script
Create file /usr/local/ffmpeg-hls/start-all-cams.sh
#!/bin/bash
CONFIG_FILE="/usr/local/ffmpeg-hls/cams.yml"
OUTPUT_ROOT="/var/www/html/cctv"
HLS_DURATION=10 # seconds per segment
TOTAL_HISTORY_MIN=10 # total minutes to keep
SEGMENTS_TO_KEEP=$(( (TOTAL_HISTORY_MIN*60) / HLS_DURATION ))
mkdir -p "$OUTPUT_ROOT"
#Generate list of active cams
python3 - <<'PYCODE' > /tmp/camlist.txt
import yaml, sys
data = yaml.safe_load(open("/usr/local/ffmpeg-hls/cams.yml"))
for cam in data:
print(f"{cam['name']}|{cam['url']}")
PYCODE
#Build array of active cam names
ACTIVE_CAMS=($(awk -F'|' '{print $1}' /tmp/camlist.txt))
#Clean up old folders not in YAML
for dir in "$OUTPUT_ROOT"/*/; do
[ -d "$dir" ] || continue
CAM_NAME=$(basename "$dir")
if [[ ! " ${ACTIVE_CAMS[@]} " =~ " ${CAM_NAME} " ]]; then
echo "Removing old folder: $dir"
rm -rf "$dir"
fi
done
#Start each active camera
while IFS="|" read -r NAME URL; do
[ -z "$NAME" ] && continue
mkdir -p "$OUTPUT_ROOT/$NAME"
echo "Starting $NAME ..."
(
while true; do
ffmpeg -hide_banner -loglevel warning \
-rtsp_transport tcp \
-i "$URL" \
-fflags +genpts -use_wallclock_as_timestamps 1 \
-an -c:v copy \
-f hls \
-hls_time $HLS_DURATION \
-hls_list_size $SEGMENTS_TO_KEEP \
-hls_flags delete_segments+append_list+program_date_time \
-hls_segment_filename "$OUTPUT_ROOT/$NAME/segment_%05d.ts" \
"$OUTPUT_ROOT/$NAME/index.m3u8"
echo "$NAME disconnected, retrying in 5s..."
sleep 5
done
) &
done < /tmp/camlist.txt
echo "All active camera streams started!"
wait
Make it executable:
sudo chmod +x /usr/local/ffmpeg-hls/start-all-cams.sh
5 — Systemd service
Create file /etc/systemd/system/cctv-hls.service
[Unit]
Description=FFmpeg HLS Auto Streams (All Cameras)
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/ffmpeg-hls/start-all-cams.sh
Restart=always
RestartSec=10
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
Apply changes
sudo nano /usr/local/ffmpeg-hls/start-all-cams.sh
# (paste script above)
sudo chmod +x /usr/local/ffmpeg-hls/start-all-cams.sh
sudo systemctl restart cctv-hls
6 — HTTPS setup with NGINX (self-signed)
Generate SSL certificate:
sudo mkdir -p /etc/ssl/cctv
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/ssl/cctv/cctv.key \
-out /etc/ssl/cctv/cctv.crt \
-subj "/CN=cctv.local"
Configure NGINX site:
Create file /etc/nginx/sites-available/cctv
server {
listen 443 ssl;
server_name _;
ssl_certificate /etc/ssl/cctv/cctv.crt;
ssl_certificate_key /etc/ssl/cctv/cctv.key;
root /var/www/html;
index index.html;
location /cctv/ {
add_header Cache-Control no-cache;
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
autoindex on;
add_header Access-Control-Allow-Origin *;
}
}
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
Enable and reload:
sudo ln -s /etc/nginx/sites-available/cctv /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl restart nginx
7 — Verify everything
sudo systemctl status cctv-hls
Then visit:
https://<server-ip>/cctv/cam1/index.m3u8
https://<server-ip>/cctv/cam2/index.m3u8
https://<server-ip>/cctv/cam3/index.m3u8