Het bufferquotum wordt overschreden

Joe Medley
Joe Medley

Als u met Media Source Extensions (MSE) werkt, zult u uiteindelijk te maken krijgen met een overvolle buffer. Wanneer dit gebeurt, krijgt u een zogenaamde QuotaExceededError . In dit artikel bespreek ik enkele manieren om ermee om te gaan.

Wat is de QuotaExceededError?

Kortom, QuotaExceededError is wat u krijgt als u te veel gegevens aan uw SourceBuffer object probeert toe te voegen. (Het toevoegen van meer SourceBuffer objecten aan een bovenliggend MediaSource element kan deze fout ook veroorzaken. Dat valt buiten het bestek van dit artikel.) Als SourceBuffer te veel gegevens bevat, zal het aanroepen van SourceBuffer.appendBuffer() het volgende bericht activeren in het Chrome-consolevenster .

Fout in de quotaconsole.

Hierover zijn een aantal zaken op te merken. Merk allereerst op dat de naam QuotaExceededError nergens in het bericht voorkomt. Om dat te zien, stelt u een breekpunt in op een locatie waar u de fout kunt opmerken en onderzoekt u deze in uw horloge- of scoopvenster. Dit heb ik hieronder weergegeven.

Quota-bewakingsvenster.

Ten tweede is er geen definitieve manier om erachter te komen hoeveel gegevens de SourceBuffer kan verwerken.

Gedrag in andere browsers

Op het moment van schrijven genereert Safari in veel van zijn builds geen QuotaExceededError . In plaats daarvan worden frames verwijderd met behulp van een tweestapsalgoritme, waarbij wordt gestopt als er voldoende ruimte is om de appendBuffer() af te handelen. Ten eerste worden frames tussen 0 en 30 seconden vóór de huidige tijd vrijgegeven in blokken van 30 seconden. Vervolgens worden frames in blokken van 30 seconden vrijgemaakt van duur naar achteren tot slechts 30 seconden na currentTime . Meer hierover leest u in een Webkit-wijzigingsset uit 2014 .

Gelukkig genereren Edge en Firefox, samen met Chrome, deze fout. Als u een andere browser gebruikt, moet u uw eigen tests uitvoeren. Hoewel het waarschijnlijk niet is wat je zou bouwen voor een echte mediaspeler, kun je met de bronbufferlimiettest van François Beaufort in ieder geval het gedrag observeren.

Hoeveel gegevens kan ik toevoegen?

Het exacte aantal varieert van browser tot browser. Omdat u niet kunt opvragen hoeveel gegevens er momenteel zijn toegevoegd, moet u bijhouden hoeveel u zelf toevoegt. Wat betreft wat je moet bekijken, hier zijn de beste gegevens die ik kan verzamelen op het moment dat ik dit schrijf. Voor Chrome zijn deze getallen bovengrenzen, wat betekent dat ze kleiner kunnen zijn als het systeem geheugendruk ondervindt.

Chroom Chromecast* Firefox Safari Rand
Video 150 MB 30 MB 100 MB 290 MB Onbekend
Audio 12 MB 2 MB 15 MB 14 MB Onbekend
  • Of een ander Chrome-apparaat met beperkt geheugen.

Dus wat moet ik doen?

Omdat de hoeveelheid ondersteunde gegevens zo sterk varieert en u de hoeveelheid gegevens niet in een SourceBuffer kunt vinden, moet u deze indirect verkrijgen door de QuotaExceededError af te handelen. Laten we nu eens kijken naar een paar manieren om dat te doen.

Er zijn verschillende manieren om met QuotaExceededError om te gaan. In werkelijkheid is een combinatie van één of meer benaderingen het beste. Uw aanpak zou moeten zijn om het werk te baseren op hoeveel u ophaalt en probeert toe te voegen buiten HTMLMediaElement.currentTime en die grootte aan te passen op basis van de QuotaExceededError . Ook het gebruik van een soort manifest, zoals een mpd-bestand (MPEG-DASH) of een m3u8-bestand (HLS), kan u helpen bij het bijhouden van de gegevens die u aan de buffer toevoegt.

Laten we nu eens kijken naar verschillende benaderingen voor het omgaan met de QuotaExceededError .

  • Verwijder overbodige gegevens en voeg ze opnieuw toe.
  • Voeg kleinere fragmenten toe.
  • Verlaag de afspeelresolutie.

Hoewel ze in combinatie kunnen worden gebruikt, zal ik ze één voor één behandelen.

Verwijder overbodige gegevens en voeg ze opnieuw toe

Eigenlijk zou deze moeten heten: "Verwijder gegevens die het minst waarschijnlijk binnenkort zullen worden gebruikt, en probeer vervolgens opnieuw de gegevens toe te voegen die waarschijnlijk binnenkort zullen worden gebruikt." Dat was een te lange titel. Je hoeft alleen maar te onthouden wat ik echt bedoel.

Het verwijderen van recente gegevens is niet zo eenvoudig als het aanroepen van SourceBuffer.remove() . Om gegevens uit SourceBuffer te verwijderen, moet de updatevlag false zijn. Als dit niet het geval is, roept u SourceBuffer.abort() aan voordat u gegevens verwijdert.

Er zijn een paar dingen waarmee u rekening moet houden bij het aanroepen van SourceBuffer.remove() .

  • Dit kan een negatief effect hebben op het afspelen. Als u bijvoorbeeld wilt dat de video snel opnieuw wordt afgespeeld of herhaald, wilt u wellicht het begin van de video niet verwijderen. Als u of de gebruiker een deel van de video zoekt waarin u gegevens heeft verwijderd, moet u die gegevens opnieuw toevoegen om aan die zoekopdracht te voldoen.
  • Verwijder zo conservatief als je kunt. Pas op voor het verwijderen van de groep frames die momenteel wordt afgespeeld, beginnend bij het hoofdframe op of vóór currentTime , omdat dit ertoe kan leiden dat het afspelen vastloopt. Dergelijke informatie moet mogelijk door de web-app uit de bytestream worden geparseerd als deze niet beschikbaar is in het manifest. Een mediamanifest of app-kennis van sleutelframe-intervallen in de media kan u helpen bij de keuze van de verwijderingsbereiken van uw app om te voorkomen dat de momenteel afgespeelde media worden verwijderd. Wat u ook verwijdert, verwijder niet de momenteel afgespeelde groep afbeeldingen of zelfs de eerste paar daarbuiten. Over het algemeen mag u de media niet na de huidige tijd verwijderen, tenzij u zeker weet dat u de media niet langer nodig heeft. Als u de afspeelkop dicht bij de afspeelkop verwijdert, kan dit een vastlopen veroorzaken.
  • Safari 9 en Safari 10 implementeren SourceBuffer.abort() niet correct . In feite genereren ze fouten die het afspelen stoppen. Gelukkig zijn er hier en hier open bugtrackers. In de tussentijd zul je hier op de een of andere manier omheen moeten werken. Shaka Player doet dit door een lege abort() -functie in die versies van Safari uit te schakelen.

Voeg kleinere fragmenten toe

Ik heb de procedure hieronder weergegeven. Dit werkt misschien niet in alle gevallen, maar heeft als voordeel dat de grootte van de kleinere brokken kan worden aangepast aan jouw wensen. Er is ook geen terugkeer naar het netwerk nodig, wat voor sommige gebruikers extra datakosten met zich mee kan brengen.

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);

Verlaag de afspeelresolutie

Dit is vergelijkbaar met het verwijderen van recente gegevens en het opnieuw toevoegen ervan. In feite kunnen de twee samen worden gedaan, hoewel het onderstaande voorbeeld alleen het verlagen van de resolutie laat zien.

Er zijn een paar dingen waarmee u rekening moet houden bij het gebruik van deze techniek:

  • U moet een nieuw initialisatiesegment toevoegen. U moet dit elke keer doen als u van weergave verandert. Het nieuwe initialisatiesegment moet voor de mediasegmenten zijn die volgen.
  • De presentatietijdstempel van de toegevoegde media moet zo goed mogelijk overeenkomen met de tijdstempel van de gegevens in de buffer, maar mag niet vooruit springen. Het overlappen van de gebufferde gegevens kan, afhankelijk van de browser, een stotterend of kortstondig vastlopen veroorzaken. Ongeacht wat u toevoegt, laat de afspeelkop niet overlappen, omdat dit fouten oplevert.
  • Zoeken kan het afspelen onderbreken. U kunt in de verleiding komen om naar een specifieke locatie te zoeken en het afspelen vanaf daar te hervatten. Houd er rekening mee dat dit een onderbreking van het afspelen veroorzaakt totdat het zoeken is voltooid.