Superamento della quota di buffering

Joe Medley
Joe Medley

Se utilizzi le estensioni Media Source (MSE), prima o poi dovrai gestire un buffer troppo pieno. In questo caso, riceverai un QuotaExceededError. In questo articolo, tratterò alcuni dei modi per affrontarla.

Che cos'è QuotaExceededError?

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

Errore della console della quota.

Tieni presente alcune cose. Innanzitutto, tieni presente che il nome QuotaExceededError non compare da nessuna parte nel messaggio. Per verificarlo, imposta un breakpoint in un punto in cui puoi rilevare l'errore ed esaminarlo nella finestra di monitoraggio o di ambito. L'ho mostrato di seguito.

Finestra di monitoraggio della quota.

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

Comportamento in altri browser

Al momento della stesura di questo articolo, Safari non genera un QuotaExceededError in molte delle sue build. Rimuove invece i fotogrammi utilizzando un algoritmo in due fasi, interrompendosi se c'è spazio sufficiente per gestire il appendBuffer(). Innanzitutto, libera i frame tra 0 e 30 secondi prima dell'ora corrente in blocchi di 30 secondi. Successivamente, libera i frame in blocchi di 30 secondi dalla durata a ritroso fino a 30 secondi dopo currentTime. Puoi scoprire di più in un cambio di Webkit del 2014.

Fortunatamente, oltre a Chrome, Edge e Firefox generano questo errore. Se utilizzi un altro browser, dovrai eseguire i test autonomamente. Anche se probabilmente non è ciò che costruiresti per un media player reale, il test del limite del buffer dell'origine di François Beaufort consente almeno di osservare il comportamento.

Quanti dati posso accodare?

Il numero esatto varia da browser a browser. Poiché non puoi eseguire query per la quantità di dati attualmente aggiunti, dovrai tenere traccia della quantità che stai aggiungendo. Per quanto riguarda i contenuti da guardare, ecco i dati migliori che posso raccogliere al momento. Per Chrome questi sono limiti superiori, cioè possono essere più piccoli quando il sistema incontra carichi di memoria.

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

Che cosa devo fare?

Poiché la quantità di dati supportati varia notevolmente e non puoi trovare la quantità di dati in un SourceBuffer, devi recuperarla indirettamente gestendo il QuotaExceededError. Vediamo alcuni modi per farlo.

Esistono diversi approcci per gestire QuotaExceededError. In realtà, è preferibile una combinazione di uno o più approcci. Il tuo approccio dovrebbe basarsi sul volume di dati che stai recuperando e che stai tentando di accodare oltre HTMLMediaElement.currentTime e sull'aggiustamento di queste dimensioni in base a QuotaExceededError. Anche l'utilizzo di un 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.

Vediamo ora diversi approcci per gestire l'QuotaExceededError.

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

Anche se possono essere utilizzati in combinazione, li esaminerò uno alla volta.

Rimuovi i dati non necessari e li aggiungi di nuovo

In realtà, questo dovrebbe essere chiamato "Rimuovi i dati meno propensi a essere utilizzati a breve e poi riprova ad aggiungere i dati che potrebbero essere utilizzati a breve". Il titolo è troppo lungo. Devi solo ricordare cosa intendo davvero.

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

Tieni presente alcune cose quando chiami SourceBuffer.remove().

  • Ciò potrebbe avere un impatto negativo sulla riproduzione. Ad esempio, se vuoi che il video venga riprodotto di nuovo o in loop a breve, potresti non voler rimuovere l'inizio del video. Analogamente, se tu o l'utente cercate una parte del video in cui sono stati rimossi i dati, dovrete aggiungerli di nuovo per soddisfare la ricerca.
  • Rimuovi il maggior numero possibile di dati in modo conservativo. Presta attenzione a rimuovere il gruppo di fotogrammi in riproduzione che inizia dal fotogramma chiave precedente o currentTime perché ciò potrebbe causare un blocco della riproduzione. Tali informazioni potrebbero dover essere analizzate dal bytestream dall'app web se non sono disponibili nel file manifest. Un manifest multimediale o la conoscenza da parte dell'app degli intervalli di keyframe nei contenuti multimediali può aiutare a scegliere gli intervalli di rimozione dell'app per evitare di rimuovere i contenuti multimediali in riproduzione. Qualsiasi elemento rimuovi, non rimuovere il gruppo di immagini attualmente in riproduzione o anche le prime alcune oltre. In genere, non rimuovere elementi oltre la data e l'ora corrente, a meno che non sia certo che i contenuti multimediali non sono più necessari. Se lo rimuovi vicino al cursore di riproduzione, potresti causare un blocco.
  • Safari 9 e Safari 10 non implementano correttamente SourceBuffer.abort(). Infatti, generano errori che interrompono la riproduzione. Fortunatamente, esistono tracker dei bug aperti qui e qui. Nel frattempo, dovrai trovare un modo per ovviare al problema. Shaka Player lo fa implementando una funzione abort() vuota su queste versioni di Safari.

Aggiungere frammenti più piccoli

Ho mostrato la procedura 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 è necessario tornare alla rete, il che potrebbe comportare costi aggiuntivi 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);

Abbassa la risoluzione di riproduzione

È simile alla rimozione dei dati recenti e al loro nuovo accodamento. In realtà, queste due operazioni possono essere eseguite insieme, anche se l'esempio riportato di seguito mostra solo la riduzione della risoluzione.

Quando utilizzi questa tecnica, tieni presente quanto segue:

  • Devi aggiungere un nuovo segmento di inizializzazione. Devi farlo ogni volta che modifichi le rappresentazioni. Il nuovo segmento di inizializzazione deve essere per i segmenti media che seguono.
  • Il timestamp di presentazione dei contenuti multimediali aggiunti deve corrispondere il più possibile al timestamp dei dati nel buffer, ma non deve saltare in avanti. L'accavallamento dei dati memorizzati nella cache può causare uno scricchiolio o un breve blocco, a seconda del browser. Indipendentemente da ciò che aggiungi, non sovrapporre il cursore di riproduzione, poiché questo causerà errori.
  • La ricerca potrebbe interrompere la riproduzione. Potresti avere la tentazione di spostarti in una località specifica e riprendere la riproduzione da lì. Tieni presente che questa operazione causerà l'interruzione della riproduzione fino al completamento della ricerca.