Come funziona lo streaming dalla webcam Reolink con Raspberry Pi e Nginx
Una guida per trasformare il flusso video RTSP di una webcam Reolink in uno streaming visibile su qualsiasi browser tramite un Raspberry Pi e il server web Nginx installato localmente.
1. Il problema da risolvere
Usiamo una webcam Reolink RLC-510A, un modello poco costoso ma che ha quanto ci serve per lo streaming live da esterno: POE (power over ethernet) e RTSP. Le webcam Reolink inviano video tramite RTSP, un protocollo usato per videosorveglianza e sistemi professionali.
Tuttavia:
❌ RTSP NON funziona nei browser.
Chrome, Firefox, Safari non sanno aprirlo.
Il web funziona invece con:
✔ HLS (HTTP Live Streaming)
✔ MPEG-DASH
Quindi serve un “traduttore”.
2. Struttura del sistema
Il sistema finale che abbiamo creato fa questo:
Ogni elemento ha una funzione precisa.
3. I protocolli spiegati in modo semplice
RTSP – il linguaggio della webcam
È usato dalle webcam IP. Ottimo per sistemi di videosorveglianza, non compatibile con i browser. Sulla Reolink il flusso video RTSP è rtsp://admin:password@192.168.1.35:554//h264Preview_01_main
RTMP – il linguaggio per i server di streaming
RTMP è un protocollo per mandare video verso un server. I browser NON lo leggono. Lo usiamo come “ponte” tra FFmpeg e Nginx.
HLS – il linguaggio del browser
È lo standard attuale per lo streaming su web. Funziona così:
-
il video viene diviso in file
.tspiccoli -
un file
.m3u8li elenca -
il browser li scarica in sequenza
È esattamente come funziona YouTube Live.
4. Ruolo del Raspberry Pi
Il Raspberry Pi svolge 3 compiti:
- Riceve il flusso RTSP dalla webcam
- Lo invia in RTMP al server interno Nginx
- Nginx lo converte in HLS
Diventa quindi un piccolo server streaming sempre acceso ed economico.
5. Lo script FFmpeg
FFmpeg è il software che prende il video RTSP e lo spedisce al server RTMP.
Script utilizzato reolink-rtsp-to-rtmp.sh da personalizzare il protocollo RTSP con il corretto ip locale della webcam e la password e la risorsa RTMP con un nome convenzionale del progetto live a cui si stalavorando:
#!/bin/bash
RTSP_URL="rtsp://admin:password@192.168.1.35:554//h264Preview_01_main"
RTMP_URL="rtmp://localhost/live/webcampergola"
exec ffmpeg -rtsp_transport tcp -i "$RTSP_URL" \
-c:v copy -c:a aac -ar 44100 -b:a 128k \
-f flv "$RTMP_URL"
Cosa fa?
-
prende il video dalla webcam
-
non lo ricodifica (risparmio CPU)
-
lo invia come RTMP a Nginx
6. Avvio automatico tramite systemd
Per avviare lo script alla partenza del Raspberry e in caso di crash occorre creare un servizio systemd in /etc/systemd/system/reolink-stream.service
Nginx riceve RTMP e produce automaticamente HLS.
Configurazione del server web locale NGINX per gestire il protocollo RTMP:
rtmp {
server {
listen 1935;
application live {
live on;
record off;
hls on;
hls_path /var/www/hls;
hls_fragment 3s;
hls_playlist_length 20s;
}
}
}
http {
server {
listen 8080;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /var/www;
add_header Cache-Control no-cache;
add_header 'Access-Control-Allow-Origin' '*' always;
}
}
}
Risultato generato:
Con HLS.js si può vedere il flusso anche su Chrome.
Codice:
Servono 3 passaggi:
✔ 1. IP fisso del Raspberry
In DHCP reservation del router scegliere un ip fisso per la webcam.
✔ 2. Port forwarding
WAN 8080 → LAN 8080 → Raspberry.:Questo non serve più se andiamo a configurare https e quindi ad abilitare la porta 443 sulla Raspberry. Vedi in calce alla pagina l’Aggiornamento.
✔ 3. DDNS (DuckDNS, No-IP…)
Per ottenere un nome Internet stabile con cui configurare il Javascript del player HTML5 sul sito web. Installare il client DDNS sul Raspberry (consigliato) Invece di configurare il DDNS sul router (che a volte è limitato), il metodo più sicuro è: uno script sul Raspberry che aggiorna regolarmente l’IP pubblico sul DDNS.
C’è bisogno del token che viene rilasciato dal servizio DNS Lo script chiama un URL periodicamente (es. ogni 5 minuti) per dire “il mio IP è questo”.
Ci vuole un piccolo script curl: /usr/local/bin/duckdns-update.sh
#!/bin/bash DOMAIN="proloco-pergola" TOKEN="IL_TUO_TOKEN" URL="https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&ip=" curl -s "$URL" > /var/log/duckdns.log date >> /var/log/duckdns.log echo "DuckDNS aggiornato" >> /var/log/duckdns.log echo "----------------" >> /var/log/duckdns.log
e una entry in crontab -e
*/5 * * * * /usr/local/bin/duckdns-update.sh >/dev/null 2>&1
Ora il Raspberry aggiornerà DuckDNS:
-
ad ogni reboot
-
ogni 5 minuti
-
ogni volta che l’IP pubblico cambia
11 Serve HTTPS
Tutto quanto sopra non funziona se non si abilita la gestione del protocollo https sul server web NGINX. Questo perchè non è possibile, oggi, avere senza conseguenze sul sito il “mixed content” ossia contenuti https (il sito fatto con Divi) e uno stream http.
WordPress è servito in HTTPS. Per evitare errore “Mixed Content”, anche il video deve essere caricato da una sorgente HTTPS. Ma il router TIM H388X spesso blocca o interferisce con le porte 80/443, impedendo a Let’s Encrypt di verificare i certificati tramite HTTP-01. Per questo abbiamo usato una soluzione alternativa e più potente: DNS-01 tramite DuckDNS.
1. Ottenere un certificato Let’s Encrypt con DNS-01 (DuckDNS)
Comando iniziale:
sudo certbot -d proloco-pergola.duckdns.org --manual --preferred-challenges dns certonly
Certbot chiede di aggiungere una entry TXT DNS. DuckDNS la accetta tramite richiesta HTTP:
https://www.duckdns.org/update?domains={YOURVALUE}&token={YOURVALUE}&txt={YOURVALUE}[&verbose=true][&clear=true]
Dove DOMAINS è il nostro dominio mappato siu duckdns.org, token è il nostro token sempre su duckdns.org e txt il valore restituito da certbot che attende fino a quando una volta fatta l’operazione e accertato che abbia avuto esito positivo non cliccate su enter e proseguite fino alla conclusione del processo di creazione dei certificati.
Una volta completato, i certificati vengono generati:
-
/etc/letsencrypt/live/proloco-pergola.duckdns.org/fullchain.pem -
/etc/letsencrypt/live/proloco-pergola.duckdns.org/privkey.pem
Validità: 90 giorni.
2. Configurazione di Nginx per servire HTTPS + HLS
Ecco il blocco essenziale da inserire in nginx.conf:
server {
listen 443 ssl;
server_name proloco-pergola.duckdns.org;
ssl_certificate /etc/letsencrypt/live/proloco-pergola.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/proloco-pergola.duckdns.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias /var/www/hls;
add_header Cache-Control no-cache;
add_header 'Access-Control-Allow-Origin' '*' always;
}
}
server {
listen 80;
server_name proloco-pergola.duckdns.org;
return 301 https://$host$request_uri;
}
Riavvio:
sudo nginx -t
sudo systemctl restart nginx
A questo punto la tua Raspberry serve lo stream in:
https://proloco-pergola.duckdns.org/hls/webcampergola.m3u8
Testabile da un cellulare in 4G.
3. Player HTML5 nel sito WordPress (con HLS.js)
Questo è il codice da inserire nella pagina Divi:
<video id="stream" controls autoplay muted playsinline width="100%"></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
var video = document.getElementById('stream');
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource('https://proloco-pergola.duckdns.org/hls/webcampergola.m3u8');
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = 'https://proloco-pergola.duckdns.org/hls/webcampergola.m3u8';
}
</script>
Funziona su:
- iPhone
- Android
- Chrome / Firefox / Safari
4. Automazione completa del rinnovo SSL
Let’s Encrypt richiede il rinnovo ogni 90 giorni.
Abbiamo creato tre script:
4.1 Auth Hook /usr/local/bin/duckdns-auth.sh
Crea TXT su DuchDNS
#!/bin/bash # DuckDNS manual auth hook for certbot DNS-01 challenge DOMAIN="proloco-pergola" TOKEN="b8851967-de68-4aa9-85b9-180eba33565a" echo "Creating TXT record on DuckDNS..." curl -s "https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&txt=$CERTBOT_VALIDATION" > /dev/null # Wait for DNS propagation echo "Waiting 30 seconds for DNS propagation..." sleep 30
Permessi: sudo chmod +x /usr/local/bin/duckdns-auth.sh
4.2 Cleanup Hook /usr/local/bin/duckdns-clean.sh
Rimuove TXT
#!/bin/bash # DuckDNS cleanup hook for certbot DNS-01 DOMAIN="proloco-pergola" TOKEN="b8851967-de68-4aa9-85b9-180eba33565a" echo "Removing TXT record from DuckDNS..." curl -s "https://www.duckdns.org/update?domains=$DOMAIN&token=$TOKEN&txt=" > /dev/null
Permessi: sudo chmod +x /usr/local/bin/duckdns-clean.sh
4.3 Script rinnovo /usr/local/bin/duckdns-renew.sh
Renew completo (giornaliero)
#!/bin/bash certbot renew \ --manual \ --preferred-challenges dns \ --manual-auth-hook /usr/local/bin/duckdns-auth.sh \ --manual-cleanup-hook /usr/local/bin/duckdns-clean.sh \ --manual-public-ip-logging-ok \ --deploy-hook "systemctl reload nginx" \ -q
Permessi: sudo chmod +x /usr/local/bin/duckdns-*.sh
5. Cron job per rinnovo automatico
Apri cron:
sudo crontab -e
Il certificato si rinnoverà ogni notte alle 4:00, completamente senza intervento manuale.
RISULTATO FINALE
✔ La webcam Reolink trasmette via RTSP
✔ FFmpeg la converte in streaming HLS
✔ Nginx serve il video in modo stabile
✔ HTTPS garantito tramite Let’s Encrypt
✔ DuckDNS aggiorna automaticamente l’IP pubblico
✔ Il player HTML5 su WordPress funziona da ogni dispositivo
✔ Compatibilità totale con browser moderni
Il riisultato è questo:
(sembra niente!)
Il sistema è:
- scalabile
- sicuro
- professionale
- ideale per webcam pubbliche
E si manterrà aggiornato in automatico.
Documentazione completa – Streaming live Pro Loco Pergola (ultimo aggiornamento)
Questo documento descrive come reinstallare da zero il sistema di streaming
della webcam della Pro Loco Pergola, includendo errori commessi, soluzioni
corrette e servizi utilizzati.
1. Obiettivo e panoramica
L’obiettivo è pubblicare su Internet il flusso video della telecamera
Reolink in modalità HLS (HTTP Live Streaming), in modo
sicuro (HTTPS) e senza dover aprire porte sul router 4G TIM.
Architettura finale:
- Telecamera Reolink (flusso
RTSP) FFmpegche converte RTSP → RTMPNginxcon moduloRTMPche genera HLS:- File
.tse index.m3u8in/var/www/hls Nginxespone/hlsin HTTPS con certificato Let’s EncryptCloudflare Tunnelpubblica il sito
https://prolocopergola.streamsenza port forwarding
URL finale dello streaming:
https://prolocopergola.stream/hls/webcampergola.m3u8
2. Servizi utilizzati (pagamento e gratuiti)
2.1 Servizi a pagamento utilizzati
- NordVPN – IP dedicato
Fornisce un IP pubblico statico (es.89.18.x.x), necessario perché:- con SIM 4G TIM si è dietro CGNAT, quindi nessuna porta è effettivamente raggiungibile dall’esterno;
- Let’s Encrypt e i servizi esterni devono raggiungere il server tramite un IP “vero”.
- Cloudflare – dominio
prolocopergola.stream
Il dominio è registrato e gestito da Cloudflare, che fornisce:- DNS affidabile;
- integrazione con Cloudflare Tunnel;
- certificati HTTPS lato Cloudflare quando serve.
2.2 Servizi NON più usati (e perché)
- DuckDNS.org
Usato all’inizio per DNS dinamico, ma:- non è gestibile da Cloudflare DNS;
- non è ideale per Let’s Encrypt quando si passa attraverso VPN/tunnel;
- non si integra con i Public Hostname di Cloudflare Tunnel.
Conclusione: sostituito da un dominio nativo Cloudflare.
- OpenVPN + DuckDNS
Tentativo iniziale: creare una VPN e usare DuckDNS per raggiungere il Raspberry.Problemi:
- Routing complesso e poco stabile;
- difficoltà nel gestire HTTPS “pulito”;
- non necessario una volta adottato Cloudflare Tunnel.
Conclusione: architettura abbandonata. Ora si usa solo NordVPN (IP dedicato) + Cloudflare Tunnel.
3. Schema architetturale finale
Reolink RTSP
↓
FFmpeg (RTSP → RTMP)
↓
Nginx + RTMP Module (app "live")
↓
HLS (segmenti .ts + playlist .m3u8 in /var/www/hls)
↓
Nginx HTTPS (server_name prolocopergola.stream, location /hls)
↓
Cloudflare Tunnel (tunnel <→> prolocopergola.stream)
↓
Internet (browser, VLC, player web)
4. Errori principali commessi e cosa NON rifare
4.1 Cercare di aprire porte sul router 4G (port forwarding)
Con una SIM 4G TIM sei dietro CGNAT. Il port forwarding sul router
non funziona perché:
- l’IP pubblico è condiviso tra molti utenti;
- non hai il controllo del NAT del provider.
Lezione: scordarsi il port forwarding con SIM 4G, usare tunnel (Cloudflare) o IP dedicato VPN.
4.2 Usare DuckDNS con Cloudflare Tunnel
Cloudflare Tunnel si integra al meglio con domini gestiti nei DNS Cloudflare.
DuckDNS non lo è, quindi:
- non si possono creare hostname supportati nativamente dal tunnel;
- certificati e routing diventano complicati.
4.3 Configurazioni miste/rotte di nginx
Alcuni problemi trovati:
nginx.confcon caratteri non UTF-8 → Certbot falliva;- blocchi
serverduplicati o fuori dal bloccohttp { ... }; aliassenza slash finale (es.alias /var/www/hlsinvece di/var/www/hls/).
4.4 Tunnel Cloudflare configurato con dominio non Cloudflare
Tentativi con *.duckdns.org e altri hostname non gestiti da Cloudflare
portavano a errori tipo:
SSL_ERROR_SYSCALL502 Bad Gateway
Soluzione corretta: usare un dominio risultante da registrazione Cloudflare
(es. prolocopergola.stream).
5. Reinstallazione da zero – Passi tecnici
5.1 Prerequisiti sul Raspberry Pi
Eseguire aggiornamenti e installare i pacchetti base:
sudo apt update
sudo apt upgrade -y
sudo apt install -y \
ffmpeg \
nginx \
libnginx-mod-rtmp \
curl
5.2 Creazione cartella HLS
sudo mkdir -p /var/www/hls
sudo chown www-data:www-data /var/www/hls
sudo chmod 755 /var/www/hls
5.3 Configurazione Nginx con modulo RTMP + HTTPS
File principale: /etc/nginx/nginx.conf
Una configurazione funzionale (da adattare se necessario) potrebbe essere:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
# RTMP block
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
record off;
# Permette al Raspberry nella LAN di pubblicare lo stream
allow publish 127.0.0.1;
allow publish 192.168.0.0/16;
deny publish all;
hls on;
hls_path /var/www/hls;
hls_fragment 3;
hls_playlist_length 20;
allow play all;
}
}
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
# Server HTTPS locale
server {
listen 443 ssl;
server_name prolocopergola.stream;
ssl_certificate /etc/letsencrypt/live/prolocopergola.stream/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/prolocopergola.stream/privkey.pem;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias /var/www/hls/;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin "*" always;
}
}
}
Verifica della configurazione:
sudo nginx -t
sudo systemctl restart nginx
6. FFmpeg – Script per RTSP → RTMP
6.1 Script shell
Posizione suggerita: /usr/local/bin/reolink-rtsp-to-rtmp.sh
#!/bin/bash
# URL RTSP della Reolink
RTSP_URL="rtsp://admin:LA_TUA_PASSWORD@192.168.1.55:554/h264Preview_01_main"
# RTMP verso Nginx RTMP locale
RTMP_URL="rtmp://localhost/live/webcampergola"
exec ffmpeg -rtsp_transport tcp -i "$RTSP_URL" \
-c:v copy -c:a aac -ar 44100 -b:a 128k \
-f flv "$RTMP_URL"
Rendere eseguibile:
sudo chmod +x /usr/local/bin/reolink-rtsp-to-rtmp.sh
7. Servizio systemd per FFmpeg
7.1 File di servizio
Posizione: /etc/systemd/system/reolink-stream.service
[Unit]
Description=Reolink - RTSP - FFmpeg - RTMP bridge
After=network-online.target nginx.service
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/reolink-rtsp-to-rtmp.sh
Restart=always
RestartSec=5
StartLimitIntervalSec=0
[Install]
WantedBy=multi-user.target
Abilitazione e avvio:
sudo systemctl daemon-reload
sudo systemctl enable --now reolink-stream.service
# Verifica
sudo systemctl status reolink-stream.service
8. Cloudflare Tunnel – Configurazione
8.1 Installazione cloudflared
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb
8.2 Login e creazione tunnel
cloudflared login
# Seguire il link nel browser e associare l'account Cloudflare
# Creazione tunnel
cloudflared tunnel create webcampergola
Annotare l’UUID del tunnel (es.
5934ef68-010c-4e61-89cf-f5e0e43c8448).
8.3 File di configurazione /etc/cloudflared/config.yml
tunnel: 5934ef68-010c-4e61-89cf-f5e0e43c8448
credentials-file: /home/mauro/.cloudflared/5934ef68-010c-4e61-89cf-f5e0e43c8448.json
origincert: /home/mauro/.cloudflared/cert.pem
ingress:
- hostname: prolocopergola.stream
service: https://localhost:443
- service: http_status:404
8.4 Servizio systemd per cloudflared
sudo cloudflared service install
sudo systemctl enable --now cloudflared
# Verifica
sudo systemctl status cloudflared
9. Test finale e comandi di controllo
9.1 Controllo generazione HLS
Verificare che i file .ts e .m3u8 siano in /var/www/hls:
ls -l /var/www/hls
9.2 Test playlist locale (sul Raspberry)
curl -k -I https://localhost/hls/webcampergola.m3u8
9.3 Test playlist pubblica via Cloudflare
curl -I https://prolocopergola.stream/hls/webcampergola.m3u8
9.4 Comandi rapidi di manutenzione
- Riavvia solo streaming FFmpeg:
sudo systemctl restart reolink-stream.service - Riavvia Nginx:
sudo systemctl restart nginx - Riavvia Cloudflare Tunnel:
sudo systemctl restart cloudflared - Riavvio totale Raspberry:
sudo reboot
10. URL finale e uso con VLC / player web
URL HLS pubblico definitivo:
https://prolocopergola.stream/hls/webcampergola.m3u8
Può essere usato:
- in VLC: Media → Apri flusso di rete → incollare l’URL;
- in un player HTML5 con
hls.js; - integrato nel sito ufficiale della Pro Loco.
Se tutti i test restituiscono HTTP 200 e la cartella HLS è popolata di
file .ts aggiornati ogni pochi secondi, il sistema è correttamente
installato e pronto per l’uso continuativo.





















Commenti recenti