Approfondimento: VideoNG

Dale Curtis
Dale Curtis

Sono Dale Curtis, l'ingegnere responsabile della riproduzione multimediale in Chromium. Il mio team è responsabile delle API web per la riproduzione di video come MSE e WebCodecs, e degli elementi interni specifici della piattaforma coinvolti nel demuxing, nella decodifica e nel rendering di audio e video.

In questo articolo, ti illustrerò l'architettura di rendering video di Chromium. Anche se alcuni dettagli sull'estensibilità sono probabilmente specifici di Chromium, la maggior parte dei concetti e dei progetti discussi qui si applica ad altri motori di rendering e persino ad app di riproduzione native.

L'architettura di riproduzione di Chromium è cambiata notevolmente nel corso degli anni. Anche se non abbiamo iniziato con l'idea di una piramide del successo come descritto nel primo post di questa serie, abbiamo seguito in definitiva passaggi simili: affidabilità, prestazioni ed esestensione.

All'inizio, il rendering video era abbastanza semplice: basta un ciclo for per scegliere quale software decodificare i frame video da inviare al compositore. Per anni questo approccio è stato sufficientemente affidabile, ma con l'aumento della complessità del web, la necessità di un maggiore rendimento ed efficienza ha portato a cambiamenti nell'architettura. Molti miglioramenti richiedevano primitive specifiche per il sistema operativo. Pertanto, la nostra architettura doveva diventare più estensibile per raggiungere tutte le piattaforme di Chromium.

Diagramma del flusso di rendering per diverse piattaforme Chromium.

Il rendering dei video può essere suddiviso in due passaggi: scegliere cosa pubblicare e pubblicare queste informazioni in modo efficiente. Nell'interesse della leggibilità, illustrerò il caricamento efficiente prima di approfondire il modo in cui Chromium sceglie cosa caricare.

Alcuni termini e layout

Poiché questo articolo si concentra sul rendering, tratterò solo brevemente gli aspetti di demuxing e decodifica della pipeline.

Byte in entrata e pacchetti strutturati in uscita.

La decodifica e il demuxing nel nostro mondo moderno attento alla sicurezza richiedono una certa attenzione. Gli analizzatori di file binari sono ambienti di destinazione completi e la riproduzione di contenuti multimediali è piena di analisi di file binari. Di conseguenza, i problemi di sicurezza nei parser di contenuti multimediali sono estremamente comuni.

Chromium adotta la difesa in profondità per ridurre il rischio di problemi di sicurezza per i nostri utenti. In termini pratici, ciò significa che il demuxing e la decodifica software avvengono sempre in un processo con privilegi ridotti, mentre la decodifica hardware avviene in un processo con privilegi sufficienti per comunicare con la GPU del sistema.

Le sandbox di Chromium per i processi di rendering, GPU e audio.

Il meccanismo di comunicazione tra processi di Chromium è chiamato Mojo. Anche se in questo articolo non entreremo nei dettagli di Mojo, in quanto livello di astrazione tra i processi, è un pilastro della pipeline multimediale estensibile di Chromium. È importante tenere presente questo aspetto durante l'esplorazione della pipeline di riproduzione, poiché fornisce informazioni sulla complessa orchestrazione dei componenti cross-process che interagiscono per ricevere, demuxare, decodificare e infine visualizzare i contenuti multimediali.

Così tanti bit

Per comprendere le pipeline di rendering video attuali, è necessario conoscere il motivo per cui i video sono speciali: la larghezza di banda. La riproduzione a 60 frame al secondo con una risoluzione di 3840 x 2160 (4K) richiede una larghezza di banda della memoria compresa tra 9 e 12 gigabit al secondo. Anche se i sistemi moderni possono avere una larghezza di banda di picco di centinaia di gigabit al secondo, la riproduzione dei video rappresenta ancora una parte sostanziale. Se non si presta attenzione, la larghezza di banda totale può moltiplicarsi facilmente a causa di copie o passaggi tra la memoria della GPU e della CPU.

L'obiettivo di qualsiasi motore di riproduzione video moderno incentrato sull'efficienza è minimizzare la larghezza di banda tra il decodificatore e il passaggio di rendering finale. Per questo motivo, il rendering dei video è in gran parte disaccoppiato dalla pipeline di rendering principale di Chromium. Nello specifico, dal punto di vista della nostra pipeline di rendering principale, il video è solo un buco di dimensioni fisse con opacità. Chromium ottiene questo risultato utilizzando un concetto chiamato surfaces, in cui ogni video parla direttamente con Viz.

Una pagina web con un buco e una freccia con la dicitura "Video qui".

A causa della popolarità del mobile computing, potenza ed efficienza sono diventate un obiettivo importante nella generazione attuale. Di conseguenza, la decodifica e il rendering sono più accoppiati che mai a livello hardware, il che fa sì che il video assomigli a un buco con opacità, persino per il sistema operativo stesso. I decodificatori a livello di piattaforma spesso forniscono solo buffer opachi che Chromium passa al sistema di composizione a livello di piattaforma sotto forma di overlay.

Una pagina web con un buco e una freccia con la dicitura "Video va qui", racchiusa in una casella che rappresenta il sistema operativo.

Ogni piattaforma ha la propria forma di overlay con cui le API di decodifica della piattaforma funzionano in modo coordinato. Windows dispone di Direct Composition e Media Foundation Transforms, macOS di CoreAnimation Layers e VideoToolbox, Android di SurfaceView e MediaCodec e Linux di VASurfaces e VA-API. Le astrazioni di Chromium per questi concetti sono gestite rispettivamente dalle interfacce OverlayProcessor e mojo::VideoDecoder.

In alcuni casi è possibile mappare questi buffer nella memoria di sistema, quindi non devono nemmeno essere opachi e non consumano larghezza di banda finché non vengono acceduti. Chromium li chiama GpuMemoryBuffers. Su Windows sono supportati da buffer DXGI, su macOS IOSurfaces, su Android AHardwareBuffers e su Linux buffer DMA. Sebbene la riproduzione dei video in genere non richieda questo accesso, questi buffer sono importanti per l'acquisizione video per garantire una larghezza di banda minima tra il dispositivo di acquisizione e gli eventuali codificatori.

Diagramma dei buffer menzionati nel testo precedente.

Poiché la GPU è spesso responsabile sia della decodifica sia della visualizzazione, l'uso di questi buffer (spesso) opachi garantisce che i dati video ad alta larghezza di banda non escano mai effettivamente dalla GPU. Come abbiamo discusso in precedenza, mantenere i dati sulla GPU è incredibilmente importante per l'efficienza, soprattutto a risoluzioni e frequenze fotogrammi elevate.

Più possiamo sfruttare le primitive del sistema operativo come gli overlay e i buffer della GPU, meno larghezza di banda viene utilizzata per spostare inutilmente i byte video. Mantenere tutto in un unico posto, dalla decodifica al rendering, può portare a un'efficienza energetica incredibile. Ad esempio, quando Chromium attivava gli overlay su macOS, il consumo energetico durante la riproduzione di video a schermo intero si dimezzava. Su altre piattaforme come Windows, Android e ChromeOS, possiamo utilizzare gli overlay anche in caso di visualizzazione non a schermo intero, risparmiando fino al 50% quasi ovunque.

Rendering

Ora che abbiamo trattato i meccanismi di caricamento ottimali, possiamo discutere di come Chromium sceglie cosa caricare. La struttura di riproduzione di Chromium utilizza un'architettura basata su "pull", in quanto ogni componente della struttura richiede i propri input da quello sottostante in ordine gerarchico. Nella parte superiore della pila si trova il rendering dei frame audio e video, seguito dalla decodifica, dal demuxing e infine dall'I/O. Ogni frame audio visualizzato fa avanzare un orologio che viene utilizzato per scegliere i frame video per il rendering se combinato con un intervallo di presentazione.

In ogni intervallo di presentazione (ogni aggiornamento del display), viene chiesto al video renderer di fornire un frame video da un CompositorFrameSink collegato al SurfaceLayer citato in precedenza. Per i contenuti con una frequenza fotogrammi inferiore alla frequenza di aggiornamento, significa che lo stesso frame viene mostrato più di una volta, mentre se la frequenza fotogrammi è superiore alla frequenza di aggiornamento, alcuni frame non vengono mai mostrati.

Sincronizzare l'audio e il video in modo piacevole per gli spettatori è molto più complesso. Per una discussione più approfondita su come viene raggiunta la fluidità ottimale dei video in Chromium, consulta Project Butter. Spiega come il rendering video può essere suddiviso in sequenze ideali che rappresentano il numero di volte in cui deve essere mostrato ogni fotogramma. Ad esempio: "1 frame ogni intervallo di visualizzazione ([1], 60 fps a 60 Hz)", "1 frame ogni 2 intervalli ([2], 30 fps a 60 Hz)", o pattern più complicati come [2:3:2:3:2] (25 fps a 60 Hz) che coprono più frame e intervalli di visualizzazione distinti. Più un renderer video si attiene a questo modello ideale, più è probabile che un utente percepisca la riproduzione come fluida.

La sequenza di demuxing, decodifica e rendering.

Sebbene la maggior parte delle piattaforme Chromium esegui il rendering fotogramma per fotogramma, non tutte lo fanno. La nostra architettura estensibile consente anche il rendering collettivo. Il rendering batch è una tecnica di efficienza in cui il compositore a livello di sistema operativo viene informato in anticipo di più frame e gestisce il loro rilascio in base a una pianificazione dei tempi fornita dall'applicazione.

Il futuro è adesso?

Ci siamo concentrati su come Chromium sfrutta le primitive del sistema operativo per offrire un'esperienza di riproduzione di prim'ordine. E i siti web che vogliono andare oltre la riproduzione di video di base? Possiamo offrire le stesse potenti primitive utilizzate da Chromium per inaugurare la prossima generazione di contenuti web?

Riteniamo che la risposta sia sì. L'estensibilità è al centro del nostro modo di pensare alla piattaforma web oggi. Stiamo collaborando con altri browser e sviluppatori per creare nuove tecnologie come WebGPU e WebCodecs in modo che gli sviluppatori web possano utilizzare le stesse primitive di Chromium quando comunicano con il sistema operativo. WebGPU offre il supporto per buffer GPU e WebCodecs offre primitive di decodifica e codifica della piattaforma compatibili con i sistemi di overlay e buffer GPU sopra menzionati.

Relazione tra WebCodecs e WebGPU.

Fine dello stream

Grazie per l'attenzione. Spero che questa sessione ti abbia aiutato a comprendere meglio i sistemi di riproduzione moderni e come Chromium supporta ogni giorno centinaia di milioni di ore di visualizzazione. Se cerchi altre letture su codec e video web moderni, ti consiglio H.264 is magic di Sid Bala, How Modern Video Players Work di Erica Beaves e Packaging award-winning shows with award-winning technology di Cyril Concolato.

Un'illustrazione (la più bella!) di Una Kravets.