Introduzione allo streaming live HTTP: HLS su Android e altro

Pubblicato: 2022-03-11

Lo streaming video è parte integrante della moderna esperienza di Internet. È ovunque: su telefoni cellulari, computer desktop, TV e persino dispositivi indossabili. Deve funzionare perfettamente su ogni dispositivo e tipo di rete, che si tratti di connessioni mobili lente, Wi-Fi, dietro firewall, ecc. HTTP Live Streaming (HLS) di Apple è stato creato esattamente pensando a queste sfide.

Quasi tutti i dispositivi moderni sono dotati di hardware moderno abbastanza veloce da riprodurre video, quindi la velocità e l'affidabilità della rete emergono come il problema più grande. Perché? Fino a pochi anni fa, il modo canonico di archiviare e pubblicare video era costituito da protocolli basati su UDP come RTP. Questo si è rivelato problematico in molti modi, per elencarne solo alcuni:

  1. È necessario un servizio server (daemon) per eseguire lo streaming di contenuti.
  2. La maggior parte dei firewall è configurata per consentire solo porte standard e tipi di traffico di rete, come http, e-mail, ecc.
  3. Se il tuo pubblico è globale, hai bisogno di una copia del tuo servizio daemon di streaming in esecuzione in tutte le principali regioni.

Naturalmente, potresti pensare che tutti questi problemi siano facili da risolvere. Basta archiviare file video (ad esempio file mp4) sul tuo server http e utilizzare il tuo servizio CDN preferito per servirli in qualsiasi parte del mondo.

Dove lo streaming video legacy fallisce

Questa è tutt'altro che la soluzione migliore per alcuni motivi, tra cui l'efficienza. Se archivi file video originali a piena risoluzione, gli utenti nelle aree rurali o in parti del mondo con scarsa connettività avranno difficoltà a goderteli. I loro lettori video faranno fatica a scaricare dati sufficienti per riprodurli in runtime.

Pertanto, è necessaria una versione speciale del file in modo che la quantità di video scaricato sia approssimativamente la stessa che può essere riprodotta. Ad esempio, se la risoluzione e la qualità del video sono tali da poter scaricare in cinque secondi altri cinque secondi di video, è ottimale. Tuttavia, se sono necessari cinque secondi per scaricare solo tre secondi di video, il lettore si fermerà e attenderà il download della parte successiva del flusso.

D'altra parte, ridurre ulteriormente la qualità e la risoluzione degraderebbe solo l'esperienza dell'utente su connessioni più veloci, poiché si risparmierebbe larghezza di banda inutilmente. Tuttavia, esiste una terza via.

Streaming bitrate adattivo

Sebbene tu possa caricare diverse versioni di video per utenti diversi, dovresti quindi avere la possibilità di controllare i loro giocatori e calcolare qual è lo streaming migliore per la loro connessione e dispositivo. Quindi, il giocatore deve passare da uno all'altro (ad esempio, quando un utente passa da 3G a WiFi). E anche allora, cosa succede se il client cambia il tipo di rete? Quindi il giocatore deve passare a un altro video, ma deve iniziare a riprodurre non dall'inizio, ma da qualche parte nel mezzo del video. Quindi, come si calcola l'intervallo di byte da richiedere?

Una cosa interessante sarebbe se i lettori video potessero rilevare i cambiamenti nel tipo di rete e nella larghezza di banda disponibile, e quindi passare in modo trasparente tra diversi flussi (dello stesso video preparato per velocità diverse) finché non trova quello migliore.

Questo è esattamente ciò che risolve lo streaming adattivo del bitrate.

Nota: questo tutorial HLS non coprirà la crittografia, le riproduzioni sincronizzate e l'IMSC1.

Cos'è l'HLS?

HTTP Live Streaming è un protocollo di streaming adattivo con bitrate introdotto da Apple nel 2009. Utilizza file m3u8 per descrivere i flussi multimediali e utilizza HTTP per la comunicazione tra il server e il client. È il protocollo di streaming multimediale predefinito per tutti i dispositivi iOS, ma può essere utilizzato su Android e browser web.

Illustrazione della copertina di HTTP Live Streaming

Gli elementi costitutivi di base di un flusso HLS sono:

  1. Playlist M3U8
  2. File multimediali per vari flussi

Playlist M3U8

Iniziamo rispondendo a una domanda di base: cosa sono i file M3U8 ?

M3U (o M3U8) è un formato di file di testo normale originariamente creato per organizzare raccolte di file MP3. Il formato è esteso per HLS, dove viene utilizzato per definire flussi multimediali. In HLS ci sono due tipi di file m3u8:

  • Playlist multimediale: contenente gli URL dei file necessari per lo streaming (cioè frammenti del video originale da riprodurre).
  • Playlist principale: contiene gli URL delle playlist multimediali che, a loro volta, contengono varianti dello stesso video preparate per diverse larghezze di banda.

Un cosiddetto URL di live streaming M3U8 non è altro che URL di file M3U8, come: https://s3-us-west-2.amazonaws.com/hls-playground/hls.m3u8.

Esempio di file M3U8 per HLS Stream

Un file M3U8 contiene un elenco di URL o percorsi di file locali con alcuni metadati aggiuntivi. Le righe di metadati iniziano con #.

Questo esempio illustra l'aspetto di un file M3U8 per un semplice flusso HLS:

 #EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:11 #EXTINF:5.215111, 00000.ts #EXTINF:10.344822, 00001.ts #EXTINF:10.344822, 00002.ts #EXTINF:9.310344, 00003.ts #EXTINF:10.344822, 00004.ts ... #EXT-X-ENDLIST
  • Le prime quattro righe sono metadati globali (intestazione) per questa playlist M3U8.
  • La EXT-X-VERSION è la versione del formato M3U8 (deve essere almeno 3 se vogliamo usare le voci EXTINF ).
  • Il tag EXT-X-TARGETDURATION contiene la durata massima di ogni "pezzo" di video. In genere, questo valore è di circa 10 secondi.
  • Il resto del documento contiene coppie di righe come:
 #EXTINF:10.344822, 00001.ts

Questo è un video "pezzo". Questo rappresenta il blocco 00001.ts che è esattamente lungo 10,344822 secondi. Quando un lettore video client deve avviare un video da un certo punto in detto video, può facilmente calcolare quale file .ts deve richiedere sommando le durate dei blocchi visualizzati in precedenza. La seconda riga può essere un nome file locale o un URL a quel file.

Il file M3U8 con i suoi file .ts rappresenta la forma più semplice di flusso HLS: una playlist multimediale.

Tieni presente che non tutti i browser possono riprodurre flussi HLS per impostazione predefinita.

Playlist master o file indice M3U8

Il precedente esempio M3U8 punta a una serie di blocchi .ts . Vengono creati dal file video originale, che viene ridimensionato codificato e suddiviso in blocchi.

Ciò significa che abbiamo ancora il problema delineato nell'introduzione: che dire dei client su reti molto lente (o insolitamente veloci)? Oppure client su reti veloci con dimensioni dello schermo molto ridotte? Non ha senso trasmettere in streaming un file alla massima risoluzione se non può essere mostrato in tutto il suo splendore sul tuo nuovo telefono brillante.

M3U8 in HSL

HLS risolve questo problema introducendo un altro “strato” di M3U8. Questo file M3U8 non conterrà puntatori a file .ts , ma ha puntatori ad altri file M3U8 che, a loro volta, contengono file video preparati in anticipo per bitrate e risoluzioni specifici.

Ecco un esempio di un tale file M3U8:

 #EXTM3U #EXT-X-STREAM-INF:BANDWIDTH=1296,RESOLUTION=640x360 https://.../640x360_1200.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=264,RESOLUTION=416x234 https://.../416x234_200.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=464,RESOLUTION=480x270 https://.../480x270_400.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1628,RESOLUTION=960x540 https://.../960x540_1500.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2628,RESOLUTION=1280x720 https://.../1280x720_2500.m3u8

Il video player selezionerà coppie di linee come:

 #EXT-X-STREAM-INF:BANDWIDTH=1296,RESOLUTION=640x360 https://.../640x360_1200.m3u8

Queste sono chiamate varianti dello stesso video preparate per diverse velocità di rete e risoluzioni dello schermo. Questo specifico file M3U8 ( 640x360_1200.m3u8 ) contiene le parti del file video ridimensionate a 640x360 pixel e preparate per bitrate di 1296kbps . Tieni presente che il bitrate riportato deve tenere conto sia dei flussi video che audio nel video.

Il video player di solito inizia la riproduzione dalla prima variante di streaming (nell'esempio precedente questo è 640x360_1200.m3u8). Per questo motivo, è necessario prestare particolare attenzione nel decidere quale variante sarà la prima nell'elenco. L'ordine delle altre varianti non è importante.

Se il primo file .ts impiega troppo tempo per essere scaricato (causando un "buffering", cioè aspettando il blocco successivo), il video player passerà a uno stream con un bitrate più piccolo. E, ovviamente, se viene caricato abbastanza velocemente significa che può passare a una variante di qualità migliore, ma solo se ha senso per la risoluzione del display.

Se il primo flusso nell'elenco dell'indice M3U8 non è il migliore, il client avrà bisogno di uno o due cicli fino a quando non si stabilirà con la variante giusta.

Quindi, ora abbiamo tre strati di HLS:

  1. File di indice M3U8 (la playlist principale) contenente i puntatori (URL) alle varianti.
  2. File M3U8 varianti (la playlist multimediale) per flussi diversi per dimensioni dello schermo e velocità di rete diverse. Contengono puntatori (URL) a file .ts.
  3. File .ts (blocchi) che sono file binari con parti del video.

Puoi guardare un esempio di file di indice M3U8 qui (di nuovo, dipende dal tuo browser/sistema operativo).

A volte, sai in anticipo che il client si trova su una rete lenta o veloce. In tal caso puoi aiutare il cliente a scegliere la variante giusta fornendo un file di indice M3U8 con una prima variante diversa. Ci sono due modi per farlo.

  • Il primo è avere più file di indice preparati per diversi tipi di rete e preparare in anticipo il client a richiedere quello giusto. Il client dovrà controllare il tipo di rete e quindi richiedere ad esempio http://.../index_wifi.m3u8 o http://.../index_mobile.m3u8 .
  • Puoi anche assicurarti che il client invii il tipo di rete come parte della richiesta http (ad esempio se è connesso a un wifi o mobile 2G/3G/...) e quindi preparare il file di indice M3U8 in modo dinamico per ogni richiesta. Solo il file index M3U8 necessita di una versione dinamica, i singoli stream (file M3U8 varianti) possono comunque essere archiviati come file statici.

Preparazione di file video per HLS

Esistono due importanti elementi costitutivi dell'HTTP Live Streaming di Apple. Uno è il modo in cui i file video vengono archiviati (da servire tramite HTTP in seguito) e l'altro sono i file di indice M3U8 che indicano al lettore (l'app client di streaming) dove ottenere quale file video.

Iniziamo con i file video. Il protocollo HLS prevede che i file video siano archiviati in blocchi più piccoli di uguale lunghezza, in genere 10 secondi ciascuno. In origine, quei file dovevano essere archiviati in file MPEG-2 TS ( .ts ) e codificati con il formato H.264 con audio in MP3, HE-AAC o AC-3.

Video HLS

Ciò significa che un video di 30 secondi verrà suddiviso in 3 file .ts più piccoli, ciascuno lungo circa 10 secondi.

Nota, l'ultima versione di HLS consente anche file .mp4 frammentati. Poiché questa è ancora una novità e alcuni lettori video devono ancora implementarla, gli esempi in questo articolo utilizzeranno i file .ts .

Fotogrammi chiave

I blocchi devono essere codificati con un fotogramma chiave all'inizio di ogni file. Ogni video contiene fotogrammi. I frame sono immagini, ma i formati video non memorizzano immagini complete, ciò richiederebbe troppo spazio su disco. Codificano solo la differenza rispetto al frame precedente. Quando salti a un punto centrale del video, il giocatore ha bisogno di un "punto di partenza" da cui applicare tutte quelle differenze per mostrare l'immagine iniziale, quindi iniziare a riprodurre il video.

Ecco perché i blocchi .ts devono avere un fotogramma chiave all'inizio. A volte i giocatori devono iniziare nel mezzo del pezzo. Il giocatore può sempre calcolare l'immagine corrente aggiungendo tutte le "differenze" dal primo fotogramma chiave. Ma, se inizia a 9 secondi dall'inizio, deve calcolare 9 secondi di "differenze". Per rendere il calcolo più veloce, è meglio creare fotogrammi chiave ogni pochi secondi (meglio cca 3s).

Punti di rottura HLS

Ci sono situazioni in cui si desidera riprodurre più clip video in successione. Un modo per farlo è unire i file video originali e quindi creare i flussi HLS con quel file, ma è problematico per diversi motivi. E se volessi mostrare un annuncio prima o dopo il tuo video? Forse non vuoi farlo per tutti gli utenti e probabilmente vuoi annunci diversi per utenti diversi. E, naturalmente, non vuoi preparare in anticipo file HLS con annunci diversi.

Per risolvere questo problema, c'è un tag #EXT-X-DISCONTINUITY che può essere utilizzato nella playlist m3u8. Questa riga dice fondamentalmente al video player di prepararsi in anticipo al fatto che da questo momento in poi i file .ts potrebbero essere creati con una configurazione diversa (ad esempio, la risoluzione potrebbe cambiare). Il giocatore dovrà ricalcolare il tutto ed eventualmente passare ad un'altra variante e dovrà essere preparato a tali punti di “discontinuità”.

Streaming live con HLS

Esistono fondamentalmente due tipi di "streaming video". Uno è Video On Demand ( VOD ) per i video registrati in anticipo e trasmessi in streaming all'utente quando decide di farlo. E c'è il Live Streaming . Anche se HLS è l'abbreviazione di HTTP Live Streaming, tutto ciò che è stato spiegato finora è stato incentrato su VOD, ma c'è anche un modo per fare live streaming con HLS.

Ci sono alcune modifiche nei file M3U8. Innanzitutto, deve essere presente un tag #EXT-X-MEDIA-SEQUENCE:1 nel file variante M3U8. Quindi il file M3U8 non deve terminare con #EXT-X-ENDLIST (che altrimenti deve essere sempre posto alla fine).

Durante la registrazione del tuo stream avrai costantemente nuovi file .ts . Devi aggiungerli nella playlist M3U8 e ogni volta che ne aggiungi uno nuovo il contatore nella #EXT-X-MEDIA-SEQUENCE:<counter> deve essere aumentato di 1.

Il video player controllerà il contatore. Se modificato dall'ultima volta, sa se ci sono nuovi blocchi da scaricare e riprodurre. Assicurati che il file M3U8 sia servito con le intestazioni no-cache, perché i client continueranno a ricaricare i file M3U8 in attesa della riproduzione di nuovi blocchi.

VTT

Un'altra caratteristica interessante per i flussi HLS è che puoi incorporare file VTT (Web Video Text Track). I file VTT possono essere utilizzati per vari usi. Ad esempio, per un lettore Web HLS è possibile specificare istantanee di immagini per varie parti del video. Quando l'utente sposta il mouse sull'area del timer video (sotto il lettore video), il lettore può mostrare istantanee da quella posizione nel video.

Un altro uso ovvio per i file VTT sono i sottotitoli. Il flusso HLS può specificare più sottotitoli per più lingue:

 #EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-,NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/eng/prog_index.m3u8"

Quindi, theprog_index.m3u8 appare come:

 #EXTM3U #EXT-X-TARGETDURATION:30 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:30, 0000.webvtt #EXTINF:30, 0001.webvtt ...

Il VTT effettivo (ad esempio 0000.webvtt ):

 WEBVTT X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000 00:00:01.000 --> 00:00:03.000 Subtitle -Unforced- (00:00:01.000) 00:00:03.000 --> 00:00:05.000 <i>...text here... -Unforced- (00:00:03.000)</i> <i>...text here...</i>

Oltre ai file VTT, Apple ha recentemente annunciato che HLS includerà il supporto per IMSC1, un nuovo formato di sottotitoli ottimizzato per la distribuzione in streaming. Il suo vantaggio più importante è che può essere disegnato usando CSS.

Strumenti di streaming live HTTP e potenziali problemi

Apple ha introdotto una serie di utili strumenti HSL, descritti più dettagliatamente nella guida HLS ufficiale.

  • Per i live streaming, Apple ha preparato uno strumento chiamato mediastreamsegmenter per creare file di segmento al volo da un flusso video in corso.
  • Un altro strumento importante è mediastreamvalidator . Controllerà le tue playlist M3U8, scaricherà i file video e segnalerà vari problemi. Ad esempio, quando il bitrate riportato non è lo stesso calcolato dai file .ts.
  • Ovviamente, quando devi codificare/decodificare/mux/demux/chunk/strip/merge/join/… file video/audio, c'è ffmpeg. Preparati a compilare le tue versioni personalizzate di ffmpeg per casi d'uso specifici.

Uno dei problemi più frequenti riscontrati nel video è la sincronizzazione dell'audio. Se trovi che l'audio in alcuni dei tuoi stream HLS non è sincronizzato con il video (ad es. un attore apre la bocca, ma noti che la voce è in anticipo o in ritardo di alcuni millisecondi), è possibile che il file video originale sia stato filmato utilizzando un framerate variabile. Assicurati di convertirlo in bitrate costante.

Se possibile, è ancora meglio assicurarsi che il software sia impostato per registrare video a un framerate costante.

Esempio di streaming live HTTP

Ho preparato un'applicazione HLS per Android che trasmette in streaming un HLS predefinito utilizzando il lettore ExoPlayer di Google. Mostrerà un video e un elenco di "eventi" HLS sotto di esso. Tali eventi includono: ogni file .ts scaricato o ogni volta che il giocatore decide di passare a un flusso di bitrate superiore o inferiore.

Esaminiamo le parti principali dell'inizializzazione del visualizzatore. Nel primo passaggio recupereremo il tipo di connessione corrente del dispositivo e utilizzeremo tali informazioni per decidere quale file m3u8 recuperare.

 String m3u8File = "hls.m3u8"; ConnectivityManager connectivity = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = connectivity.getActiveNetworkInfo(); if (activeNetwork != null && activeNetwork.isConnectedOrConnecting()) { int type = activeNetwork.getType(); int subType = activeNetwork.getSubtype(); if (type == ConnectivityManager.TYPE_MOBILE && subType == TelephonyManager.NETWORK_TYPE_GPRS) { m3u8File = "hls_gprs.m3u8"; } } String m3u8URL = "https://s3-us-west-2.amazonaws.com/hls-playground/" + m3u8File;

Nota che questo non è strettamente necessario. Il giocatore HLS si adatterà sempre alla variante HLS corretta dopo alcuni blocchi, ma ciò significa che nei primi 5-20 secondi l'utente potrebbe non guardare la variante ideale dello stream.

Ricorda, la prima variante nel file m3u8 è quella con cui inizierà il visualizzatore. Poiché siamo sul lato client e siamo in grado di rilevare il tipo di connessione, possiamo almeno cercare di evitare il passaggio da una variante all'altra del giocatore iniziale richiedendo il file m3u8 preparato in anticipo per questo tipo di connessione.

Nel passaggio successivo, inizializziamo e avviamo il nostro lettore HLS:

 Handler mainHandler = new Handler(); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder() .setEventListener(mainHandler, bandwidthMeterEventListener) .build(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); LoadControl loadControl = new DefaultLoadControl(); SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);

Quindi prepariamo il lettore e lo alimentiamo con l'm3u8 giusto per questo tipo di connessione di rete:

 DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "example-hls-app"), bandwidthMeter); HlsMediaSource videoSource = new HlsMediaSource(Uri.parse(m3u8URL), dataSourceFactory, 5, mainHandler, eventListener); player.prepare(videoSource);

E questo è il risultato:

Esempio di streaming HLS in Android

Compatibilità del browser HLS, sviluppi futuri

Apple richiede che le app di streaming video su iOS utilizzino HLS se i video sono più lunghi di 10 minuti o più grandi di 5 MB. Questo di per sé è una garanzia che HLS è qui per restare. C'erano alcune preoccupazioni su HLS e MPEG-DASH e quale sarà il vincitore nell'arena dei browser web. HLS non è implementato in tutti i browser moderni (probabilmente l'hai notato se hai fatto clic sui precedenti esempi di URL m3u8). Su Android, ad esempio, nelle versioni inferiori alla 4.0 non funzionerà affatto. Dalla 4.1 alla 4.4 funziona solo parzialmente (per esempio manca l'audio, o manca il video ma l'audio funziona).

Ma questa "battaglia" è diventata leggermente più semplice di recente. Apple ha annunciato che il nuovo protocollo HLS consentirà file mp4 frammentati ( fMP4 ). In precedenza, se volevi avere sia il supporto HLS che MPEG-DASH, dovevi codificare i tuoi video due volte. Ora sarai in grado di riutilizzare gli stessi file video e riconfezionare solo i file di metadati ( .m3u8 per HLS e .mpd per MPEG-DASH).

Un altro annuncio recente è il supporto per High Efficiency Video Codec (HEVC). Se utilizzato, deve essere impacchettato in file mp4 frammentati. E questo probabilmente significa che il futuro di HLS è fMP4 .

La situazione attuale nel mondo dei browser è che solo alcune implementazioni del browser del tag <video> riprodurranno HLS fuori dagli schemi. Ma ci sono soluzioni open source e commerciali che offrono compatibilità HLS. La maggior parte di essi offre HLS con un fallback Flash, ma ci sono alcune implementazioni completamente scritte in JavaScript.

Avvolgendo

Questo articolo si concentra specificamente sullo streaming live HTTP, ma concettualmente può anche essere letto come una spiegazione del funzionamento dell'ABS (Adaptive Bitrate Streaming). In conclusione, possiamo dire che HLS è una tecnologia che risolve numerosi problemi importanti nello streaming video:

  • Semplifica la memorizzazione dei file video
  • CDN
  • I giocatori client gestiscono diverse larghezze di banda del client e passano da uno stream all'altro
  • Sottotitoli, crittografia, riproduzioni sincronizzate e altre funzionalità non trattate in questo articolo

Indipendentemente dal fatto che si finisca per utilizzare HLS o MPEG-DASH, entrambi i protocolli dovrebbero offrire funzionalità simili e, con l'introduzione di mp4 frammentato (fMP4) in HLS, è possibile utilizzare gli stessi file video. Ciò significa che nella maggior parte dei casi dovrai comprendere le basi di entrambi i protocolli. Fortunatamente, sembrano muoversi nella stessa direzione, il che dovrebbe renderli più facili da padroneggiare.