unfoxo

Ho installato una webcam pubblica sul tetto di casa mia

Nel 2008, quando l’Italia ha spento il segnale televisivo analogico, il governo aveva promesso a tutti che sarebbe bastato comprare un nuovo televisore per continuare a guardare la TV, tenendo la vecchia antenna. In realtà, il trasmettitore della mia zona non ha mai funzionato bene, costringendo tutti a passare al satellite e presto i pali sui tetti sono stati abbandonati, sostituiti da parabole sui balconi.

Mio padre fece lo stesso, e scoprendo il palo appena liberato, me ne appropriai subito per fare ogni genere di esperimenti, soprattutto con antenne e radio fatte in casa. Sedici anni dopo ho montato una telecamera in cima al palo e l’ho resa disponibile online affinché tutti potessero vederla.

Un collage della vista della telecamera, con il palo in primo piano

Il panorama corrente

La maggior parte delle telecamere online funziona in modo semplice: un singolo file JPEG pubblicato su una pagina web, con un timestamp, proveniente direttamente dalla cartella cgi-bin di una vecchia telecamera. Altre utilizzano segmenti video di quindici secondi in loop, aggiornati ogni pochi minuti, per dare l’impressione di un video in tempo reale.

Alcune fotocamere a caso che mostrano diversi tipi di testi.

Nonostante questo approccio funzioni e sia sufficiente per semplicemente “vedere” un panorama, volevo qualcosa che desse una sensazione di diretta. Ciò significava trovare una videocamera che non solo fosse di ottima qualità, ma che fornisse anche un flusso pulito con cui poter lavorare.

Videocamere di “videosorveglianza”

Purtroppo, la maggior parte delle fotocamere consumer attualmente in commercio è vincolata all’ecosistema del proprio produttore e non dispone di un’interfaccia web, oppure ne ha una molto limitata. Hanno bisogno di un server, funzionano solo se si possiede un account e, prima o poi, saranno accessibili solo tramite abbonamento una volta che il produttore deciderà di averne avuto abbastanza.

Il mio obiettivo non è gestire un sistema di sicurezza, quindi non mi servono funzioni come registrazione, rilevamento dei movimenti, archiviazione o altre funzionalità “AI”. Ho scelto una telecamera del genere perché sono molto diffuse e abbastanza robuste da resistere per anni su un palo esposto alle intemperie, e la stessa risoluzione necessaria per riprendere il volto di un ladro farà in modo che ogni dettaglio del paesaggio sia ben visibile.

Fortunatamente, nel 2008, Axis, Bosch e Sony hanno creato ONVIF, “Open Network Video Interface Forum”. L’idea è semplice: qualsiasi telecamera ONVIF espone il proprio flusso in modo standard e supporta il rilevamento e la configurazione da parte di altri dispositivi compatibili.

Quindi, ho cercato una telecamera ONVIF.

Sony è ancora il re

Sebbene la maggior parte dei sensori sia in grado di produrre immagini di buona qualità durante il giorno, uno degli obiettivi di questa telecamera è quello di riprendere il tramonto e fornire immagini nitide del porto vicino durante la notte. Ciò richiede una buona gamma dinamica (altrimenti il sole oscurerebbe tutto) e algoritmi di regolazione automatica efficienti, in modo da garantire immagini di buona qualità in qualsiasi condizione di luce.

Sony è ancora il re nel settore dei sensori e produce una linea specializzata chiamata “Sony Starvis”. Questi sensori offrono una gamma dinamica straordinaria e una visione notturna in grado di mostrare dettagli e colori anche in condizioni di oscurità quasi totale.

La fotocamera offre una buona qualità dell'immagine sia di giorno che al crepuscolo e di notte.

In realtà, però, non è necessario acquistare una fotocamera Sony. Questo sensore viene fornito da Sony ai produttori di videocamere, quindi è possibile scegliere tra tantissime marche diverse: solitamente il modello del sensore è indicato nella scheda del prodotto, o basta chiedere al produttore.

PoE

L’uso di un iniettore PoE comporta il passaggio di un unico cavo che fornisce sia l’alimentazione che dati. Ciò semplifica l’installazione e garantisce che tutto sia IP67. Inoltre, sebbene le telecamere siano progettate per rimanere sempre accese, il bit rot è sempre in agguato e molti switch PoE dispongono di un sistema di riavvio automatico nel caso la videocamera non trasmetta dati per un certo periodo di tempo.

In questo modo, i blocchi del software (e ce ne sono stati alcuni in questi tre anni) si riducono ad un reboot e a un paio di minuti di downtime.

La scelta

Con un budget piuttosto limitato e la voglia di installarla il prima possibile, ho acquistato una telecamera PTZ (pan, tilt, zoom, orientabile a distanza) prodotta da Reolink: la RLC-830A (ora fuori produzione). Rispetto ad altre marche, offre un’ottima qualità a un prezzo ragionevole e consente comunque il controllo locale.

Questa scelta ha comportato ulteriori vantaggi: avevo già lavorato con loro in passato (utilizzandoli in modo improprio come telecamere per machine vision), sapevo che il sensore era esattamente quello di cui avevo bisogno e, sebbene ONVIF fosse già sufficiente, era disponibile anche un’API aggiuntiva ben documentata.

La telecamera Reolink, appena montata sul palo.

Proteggere e servire il video

Sebbene la telecamera consenta l’accesso a più utenti o ospiti tramite il pannello integrato, rendere visibile l’intera interfaccia della telecamera solo per mostrare l’immagine è estremamente insicuro e poco pratico da gestire. Grazie a ONVIF, tuttavia, otteniamo uno streaming di buona qualità trasmesso tramite RTSP (“Real Time Streaming Protocol”) che non richiede alcun accesso.

Molti player supportano RTSP e, in teoria, sarebbe possibile trasmetterlo direttamente esponendo la videocamera. Tuttavia, ci sono alcuni problemi:

  • I browser non supportano RTSP in modo nativo. Lo streaming RTSP significherebbe dover utilizzare un app esterna o un relay per convertirlo in WebSocket
  • Le telecamere non sono progettate per fornire molti flussi simultanei. Non ho testato quanti ne possa gestire questa telecamera, ma presumo non molti.
  • Ogni flusso RTSP si somma alla larghezza di banda in upload
  • Botnet! Esistono sciami di dispositivi IoT con connessioni Internet prese in ostaggio da malintenzionati che le usano per lanciare attacchi DDoS.

Inoltre, a causa della sua distanza dalla fibra ad alta velocità, questa telecamera è collegata a una connessione Internet che fornisce solo 30 Mbps in uplink. Anche se la larghezza di banda necessaria per servire un panorama quasi statico non è enorme, basterebbero solo un paio di client connessi a saturare l’uplink della connessione – che, tra l’altro, è condivisa anche con l’utilizzo normale di internet a casa.

Per questo motivo, ho preso ispirazione dai giganti dello streaming e ho utilizzato la tecnologia che alimenta Youtube, Netflix e qualsiasi IPTV al mondo: HLS (“HTTP Live Streaming”).

HLS verso il resto

Lo streaming tradizionale funziona creando un buffer circolare: immaginate uno spezzone di nastro ad anello su cui la telecamera registra continuamente. Ogni utente che desidera vedere il video si connette al server, e il server inizia a inviare i dati a partire dalla posizione dell’utente all’interno del ciclo.

Il server deve tenere traccia della posizione di ogni connessione e inviare i pacchetti in base alle esigenze del client. La maggior parte del lavoro è svolta dal server.

HLS, invece, sposta il lavoro sul client: lo streaming viene suddiviso in spezzoni da 5-15 secondi con un ID sempre crescente, a cui viene associato un file (manifest) che indica dove si trovano gli spezzoni e come riprodurli.

Questo trasforma un singolo flusso continuo in tanti piccoli file statici che rendono la distribuzione e il caching su larga scala molto efficaci, poiché i server non devono nemmeno supportare lo streaming video né sapere in quale posizione si trovano gli utenti.

Il manifest, nel caso di uno streaming live, contiene i segmenti più recenti che sono stati prodotti. Riprodurre lo streaming è molto semplice:

  1. Scarica il manifesto
  2. Controlla l’ID dell’ultimo segmento generato
  3. Scarica e riproduci il segmento
  4. Torna al punto 11

Tutto avviene tramite HTTPS, quindi non solo è compatibile con la maggior parte, se non tutti, i browser moderni, ma viene anche raramente bloccato dai firewall aziendali.

Utilizzando ffmpeg è facile trasformare uno streaming RTSP in HLS. Basta fornirgli lo streaming di origine e una cartella di destinazione, e verrà fatto tutto al volo, senza ricodifica:

$ ffmpeg -an -i rtsp://[...] -c:v copy -f hls \
-start_number 0 -hls_time 5 -hls_list_size 5 -hls_flags delete_segments \
/tmp/videodata/index.m3u8

Ecco cosa fa il comando, pezzo per pezzo:

  • -an: rimuovi l’audio
  • -i rtsp://[...]: utilizza questo stream rtsp come input
  • -c:v copy: copia il video (senza ricodificarlo) – in questo modo azzeriamo l’uso di CPU e RAM
  • -f hls: usiamo il formato HLS
  • -start_number 0: numerando i segmenti dal numero 0 in poi…
  • -hls_time 5: …e rendendoli lunghi 5 secondi
  • -hls_list_size 5: scrivi gli ultimi 5 segmenti nel manifest…
  • -hls_flags delete_segments: …e cancella i più vecchi
  • /tmp/videodata/index.m3u8: mettendo il manifest qui

Questo comando viene eseguito in modo continuo e crea la struttura necessaria nella directory /tmp/videodata/: il manifest (index.m3u8) e una serie di segmenti chiamati indexXXX.ts, dove XXX rappresenta l’ID.

Ad esempio, ecco come appare la cartella dopo circa mezzo minuto:

$ ls /tmp/videodata/
index1.ts  index2.ts  index3.ts  index4.ts  index5.ts  index6.ts  index.m3u8

Il contenuto del manifesto è chiaro:


#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:2
#EXTINF:5.999978,
index2.ts
#EXTINF:3.999978,
index3.ts
#EXTINF:5.999989,
index4.ts
#EXTINF:3.999978,
index5.ts
#EXTINF:5.999978,
index6.ts

Tralasciando l’inizio del file che indica la versione del manifest, è presente TARGETDURATION (che comunica al lettore la durata prevista dei segmenti, in modo che possa visualizzare la barra in basso), MEDIA-SEQUENCE (ID del segmento iniziale) e poi una lista di cinque segmenti, tutti con il loro nome e la durata.

Il MEDIA-SEQUENCE è necessario perché il browser deve conoscere l’ID di ogni segmento in modo da non saltare o riprodurre i segmenti due volte. Notate come, nonostante non sia più incluso nel manifesto, “index1.ts” esista ancora sul disco. Questo serve ad assicurarsi che ogni client abbia finito di riprodurre quel pezzo prima che venga cancellato.

Noterete anche che non tutti i file hanno la lunghezza esatta che abbiamo specificato nel comando, e questo è normale: il video può essere suddiviso solo in corrispondenza dei keyframes, che vengono determinati dalla telecamera in base alla quantità di movimento e altre impostazioni di codifica.

Dividere lo stream in punti diversi senza reencodarlo lo corromperebbe, creando un risultato identico al datamoshing. Poiché abbiamo specificato -c:v copy, ffmpeg rifiuta di dividere in punti non validi, ottenendo lunghezze dei segmenti leggermente variabili.

Questa cartella può essere servita con qualsiasi server web. Un player recupererà automaticamente i segmenti e li riassemblerà lato client.

Distribuzione dello streaming

Mentre ffmpeg è in esecuzione su un piccolo server situato nella stessa posizione della telecamera, il sito web vero e proprio è ospitato su un VPS con un uplink gigabit. In questo contesto, quando parlo di “frontend” mi riferisco al proxy presente sul VPS (il server Nginx) che si trova tra gli utenti e la telecamera.

Tutto, tranne la generazione effettiva del video, viene gestito da questo frontend. Quando gli utenti richiedono segmenti video, il frontend li recupera dal backend, li memorizza nella cache e li distribuisce.

Tuttavia, configurare un semplice server di caching non è sufficiente. Lo streaming live presenta un problema di tempistica unico: appena creato un nuovo segmento, tutti gli spettatori lo scaricheranno esattamente nello stesso momento. Finito di riprodurlo, il segmento sarà immediatamente obsoleto e nessun’altro lo richiederà più.

Ecco perché dobbiamo ottimizzare il proxy correttamente, altrimenti c’è il rischio che i segmenti vengano memorizzati nella cache solo dopo che tutti hanno smesso di richiederli.

Thundering herd as a service

L’obiettivo di un server web con cache è quello di fornire le risorse ai visitatori il più rapidamente possibile. Per questo motivo, se più utenti richiedono contemporaneamente una risorsa che non è ancora presente nella cache, un server standard potrebbe aprire più connessioni dirette al backend per fungere da proxy, salvando il file (e fornendolo dalla cache) solo una volta che la prima connessione ha completato il download.

Ciò è altamente dannoso per uno streaming HLS. Se 50 persone richiedono un segmento aggiornato nello stesso momento, il server aprirà 50 connessioni simultanee al backend.

Su un uplink da 30 Mbps, lo streaming di quel segmento di 5 secondi verso quelle connessioni richiederà molto più di 5 secondi. Di conseguenza, il segmento sarà già obsoleto nel momento in cui verrà effettivamente memorizzato nella cache, sprecando larghezza di banda e causando un buffering infinito per tutti.

Vogliamo invece che il server recuperi il segmento dalla videocamera una sola volta, lo salvi e poi fornisca quella copia memorizzata nella cache a tutti gli altri utenti. Questo è esattamente ciò che fa la direttiva proxy_cache_lock di Nginx.

Quando è abilitata, se più client richiedono lo stesso file non memorizzato nella cache, il server inoltrerà solo la prima richiesta al backend e metterà in attesa le altre. Una volta che la prima richiesta avrà terminato il download e il file sarà nella cache, il server sbloccherà il resto delle richieste, che verranno completate attingendo dalla cache.

In questo modo, il backend trasmette ogni segmento solo una volta e il server front-end può gestire comodamente centinaia di spettatori simultanei senza mai saturare l’uplink. Questa configurazione trasforma essenzialmente una connessione economica residenziale da 30 Mbps in uno streaming in grado di gestire centinaia di spettatori, risparmiando una linea dedicata o costosi aggiornamenti.

Un grafico che mostra l'aumento della velocità per connessione dopo l'attivazione della cache

La differenza è notevole. Monitorando una singola connessione e attivando proxy_cache_lock, la velocità è immediatamente passata da 50 kbit/s (in fase di buffering) a 300 kbit/s, ovvero esattamente il bitrate necessario per uno streaming in tempo reale.

Conclusioni

Con la webcam online e funzionante, non restava che condividerla. I primi utenti sono arrivati grazie al passaparola: amici, parenti e loro conoscenti. Il traffico era prevalentemente locale e immagino che la webcam venisse utilizzata per controllare il tempo o per guardare l’alba e il tramonto. Ricevevo screenshot quotidiani e bastava anche un breve periodo di inattività perché il LUG (“Linux User Group”) locale di utenti Linux si attivasse in pochi minuti, segnalando il problema.

Volevo renderla realmente pubblica. Ho quindi cercato “webcam meteo online” e ho trovato Windy, una sito di meteo che mostra webcam e accetta anche segnalazioni. Dopo averla aggiunta lì, la webcam si è diffusa ed è stata inserita in modo organico in vari portali e app meteo. A volte era elencata sotto la città corretta, altre volte sotto il nome di una città vicina.

Mentre alcune piattaforme (purtroppo!) mostrano solo l’immagine preview.jpg, altre rimandano effettivamente al sito web. Il traffico che arriva è… curioso.

Il picco di traffico si verifica principalmente all’alba e al tramonto, con visualizzazioni provenienti sia da utenti locali che da luoghi come Germania, Russia o Francia (in particolare, la Costa Azzurra).

Visualizzazioni giornaliere, per paese, in Europa.

Eventi nelle vicinanze, come il Festival di Sanremo e la gara Milano-Sanremo generano picchi evidenti di visualizzazioni, così come eventi meteorologici quali forti tempeste o cicli lunari insoliti (curiosamente, la luna riesce a saturare il video meglio del sole).

A un certo punto, ho notato che qualcuno dalla Germania guardava lo streaming per ore ogni giorno, iniziando esattamente alle 9:00 e finendo alle 17:00. Controllando l’User-Agent, sembrava una Smart TV. Immagino che usasse la webcam come quadro vivente, o forse aveva semplicemente dimenticato di spegnere la TV.

Se volete dare un’occhiata alla telecamera, la trovate qui.

Grazie della lettura.


  1. … controllando che il prossimo segmento sia effettivamente sucessivo a quello precedente, per evitare salti e ripetizioni di segmenti