Superamento della quota di buffering

Joe Medley
Mario Bianchi

Se utilizzi Media Source Extensions (MSE), alla fine dovrete gestire un buffer troppo pieno. In questo caso, otterrai un QuotaExceededError. In questo articolo, parlerò di alcuni modi per risolvere il problema.

Che cos'è QuotaExceededError?

In pratica, QuotaExceededError è quello che ottieni se provi ad aggiungere troppi dati all'oggetto SourceBuffer. Anche l'aggiunta di altri oggetti SourceBuffer a un elemento MediaSource principale può generare questo errore. Questo non rientra nell'ambito di questo articolo. Se SourceBuffer contiene troppi dati, la chiamata a SourceBuffer.appendBuffer() attiverà il seguente messaggio nella finestra della console Chrome.

Errore console quota.

Ci sono alcuni aspetti da considerare in merito. Innanzitutto, tieni presente che il nome QuotaExceededError non compare in nessun punto del messaggio. Per verificarlo, imposta un punto di interruzione in una posizione in cui puoi rilevare l'errore ed esaminarlo nella finestra di controllo o ambito. come illustrato di seguito.

Finestra di controllo della quota.

In secondo luogo, non esiste un modo definitivo per scoprire quanti dati può gestire SourceBuffer.

Comportamento in altri browser

Al momento della scrittura, Safari non genera un QuotaExceededError in molte delle sue build. Rimuove i frame utilizzando un algoritmo a due passaggi, interrompendosi se c'è abbastanza spazio per gestire appendBuffer(). Per prima cosa, libera i frame da 0 a 30 secondi prima dell'ora attuale in blocchi di 30 secondi. In seguito, vengono liberati i frame in blocchi di 30 secondi dalla durata a ritroso fino a 30 secondi dopo il giorno currentTime. Puoi leggere ulteriori informazioni in merito in un set di modifiche Webkit del 2014.

Fortunatamente, insieme a Chrome, Edge e Firefox ricevono questo errore. Se usi un altro browser, dovrai eseguire i tuoi test. Anche se probabilmente non è quello che creeresti per un media player reale, il test del limite del buffer del codice sorgente di François Beaufort ti consente almeno di osservare il comportamento.

Quanti dati posso aggiungere?

Il numero esatto varia in base al browser. Poiché non puoi eseguire query sulla quantità di dati attualmente aggiunti, devi tenere traccia di quanto stai aggiungendo. Per quanto riguarda i contenuti da guardare, ecco i dati migliori che posso raccogliere al momento della stesura di questo documento. Per Chrome questi numeri sono limiti superiori, ovvero possono essere inferiori quando il sistema rileva pressione in memoria.

Chrome Chromecast* Firefox Safari Perimetrale
Video 150MB 30MB 100 MB 290MB Sconosciuto
Audio 12MB 2 MB 15 MB 14MB Sconosciuto
  • Oppure su un altro dispositivo Chrome con memoria limitata.

Che cosa posso fare?

Poiché la quantità di dati supportati varia molto e non riesci a trovare la quantità di dati in un SourceBuffer, devi ottenerli indirettamente gestendo il QuotaExceededError. Vediamo alcuni modi per farlo.

Esistono diversi approcci per gestire QuotaExceededError. In realtà, la combinazione di uno o più approcci è l'opzione migliore. L'approccio dovrebbe basare il lavoro su quanto stai recuperando e su quanto stai recuperando, oltre a HTMLMediaElement.currentTime, e modificare questa dimensione in base a QuotaExceededError. Anche l'utilizzo di un file manifest di qualche tipo, ad esempio un file mpd (MPEG-DASH) o un file m3u8 (HLS), può aiutarti a tenere traccia dei dati che aggiungi al buffer.

Esaminiamo ora diversi approcci per gestire QuotaExceededError.

  • Rimuovi i dati non necessari e aggiungili di nuovo.
  • Aggiungi frammenti più piccoli.
  • Riduci la risoluzione di riproduzione.

Anche se possono essere usati insieme, ne parlerò uno alla volta.

Rimuovi i dati non necessari e aggiungi di nuovo

In realtà questo dovrebbe essere chiamato "Rimuovi i dati con minore probabilità di essere utilizzati a breve e poi riprova ad aggiungere i dati che probabilmente verranno utilizzati a breve". Il titolo era troppo lungo. Dovrai solo ricordare cosa intendo davvero.

La rimozione dei dati recenti non è semplice come chiamare SourceBuffer.remove(). Per rimuovere i dati da SourceBuffer, il flag di aggiornamento deve essere false. In caso contrario, chiama SourceBuffer.abort() prima di rimuovere eventuali dati.

Ci sono alcune cose da tenere presenti quando chiami il numero SourceBuffer.remove().

  • Ciò potrebbe avere un impatto negativo sulla riproduzione. Ad esempio, se vuoi che il video venga riprodotto o riprodotto in loop a breve, non puoi rimuovere la parte iniziale. In modo simile, se tu o l'utente cercate una parte del video in cui hai rimosso dei dati, dovrai aggiungere di nuovo questi dati per soddisfare la richiesta.
  • Rimuovi nel modo più conservativo possibile. Fai attenzione a rimuovere il gruppo di frame attualmente in riproduzione a partire dal fotogramma chiave currentTime o prima, in quanto la riproduzione potrebbe bloccarsi. Potrebbe essere necessario analizzare queste informazioni dal bytestream dall'app web se non sono disponibili nel manifest. Un manifest multimediale o una conoscenza dell'app relativa agli intervalli dei fotogrammi chiave nei contenuti multimediali può aiutarti a scegliere gli intervalli di rimozione da parte dell'app per impedire la rimozione dei contenuti multimediali attualmente in riproduzione. Qualunque sia la tua rimozione, non rimuovere il gruppo di immagini attualmente in riproduzione o neanche le prime immagini successive. In genere, non rimuovere i contenuti oltre l'ora attuale, a meno che tu non abbia la certezza che i contenuti multimediali non siano più necessari. Se lo rimuovi vicino alla testina di riproduzione, si potrebbe verificare un blocco.
  • Safari 9 e Safari 10 non implementano correttamente SourceBuffer.abort(). Di fatto, generano errori che interromperanno la riproduzione. Fortunatamente, sono disponibili strumenti di monitoraggio dei bug aperti qui e qui. Nel frattempo, dovrai risolvere il problema in qualche modo. Shaka Player lo fa eliminando una funzione abort() vuota su quelle versioni di Safari.

Aggiungi frammenti più piccoli

La procedura è riportata di seguito. Questa operazione potrebbe non funzionare in tutti i casi, ma ha il vantaggio che le dimensioni dei blocchi più piccoli possono essere regolate in base alle tue esigenze. Inoltre, non richiede di tornare alla rete, il che potrebbe comportare costi aggiuntivi per i dati per alcuni utenti.

const pieces = new Uint8Array([data]);
(function appendFragments(pieces) {
    if (sourceBuffer.updating) {
    return;
    }
    pieces.forEach(piece => {
    try {
        sourceBuffer.appendBuffer(piece);
    }
    catch e {
        if (e.name !== 'QuotaExceededError') {
        throw e;
        }

        // Reduction schedule: 80%, 60%, 40%, 20%, 16%, 12%, 8%, 4%, fail.
        const reduction = pieces[0].byteLength * 0.8;
        if (reduction / data.byteLength < 0.04) {
        throw new Error('MediaSource threw QuotaExceededError too many times');
        }
        const newPieces = [
        pieces[0].slice(0, reduction),
        pieces[0].slice(reduction, pieces[0].byteLength)
        ];
        pieces.splice(0, 1, newPieces[0], newPieces[1]);
        appendBuffer(pieces);  
    }
    });
})(pieces);

Ridurre la risoluzione di riproduzione

Questa operazione è simile alla rimozione dei dati recenti e alla riaggiunta. In realtà, le due cose possono essere eseguite insieme, anche se l'esempio seguente mostra solo una riduzione della risoluzione.

Quando utilizzi questa tecnica, tieni presente quanto segue:

  • Devi aggiungere un nuovo segmento di inizializzazione. Devi farlo ogni volta che cambi le rappresentazioni. Il nuovo segmento di inizializzazione deve essere destinato ai segmenti multimediali successivi.
  • Il timestamp di presentazione dei contenuti multimediali aggiunti deve corrispondere il più fedelmente possibile al timestamp dei dati nel buffer, ma non va avanti. La sovrapposizione dei dati nel buffer potrebbe causare interruzioni o brevi blocchi, a seconda del browser. Indipendentemente dall'elemento aggiunto, non sovrapporre la testina di riproduzione perché genereranno errori.
  • La ricerca potrebbe interrompere la riproduzione. Potresti avere la tentazione di cercare una posizione specifica e riprendere la riproduzione da lì. Tieni presente che questa operazione causerà l'interruzione della riproduzione fino al completamento della ricerca.