Wenn du mit Media Source Extensions (MSE) arbeitest, musst du irgendwann mit einem übervollen Puffer umgehen. In diesem Fall erhalten Sie eine sogenannte QuotaExceededError
. In diesem Artikel zeige ich Ihnen, wie Sie damit umgehen können.
Was ist QuotaExceededError?
Grundsätzlich erhalten Sie QuotaExceededError
, wenn Sie versuchen, dem SourceBuffer
-Objekt zu viele Daten hinzuzufügen. (Dieser Fehler kann auch auftreten, wenn einem übergeordneten MediaSource
-Element weitere SourceBuffer
-Objekte hinzugefügt werden. Das würde den Rahmen
dieses Artikels sprengen.) Wenn SourceBuffer
zu viele Daten enthält, wird durch das Aufrufen von SourceBuffer.appendBuffer()
die folgende Meldung im Chrome-Konsolenfenster ausgelöst.
Dabei sind einige Dinge zu beachten. Beachten Sie zuerst, dass der Name QuotaExceededError
in der Nachricht nicht vorkommt. Um dies zu sehen, legen Sie einen Haltepunkt an einer Stelle fest, an der Sie den Fehler abfangen und im Beobachtungs- oder Bereichsfenster untersuchen können. Das habe ich unten dargestellt.
Zweitens gibt es keine definitive Möglichkeit, herauszufinden, wie viele Daten SourceBuffer
verarbeiten kann.
Verhalten in anderen Browsern
Zum Zeitpunkt der Erstellung dieses Artikels wird in vielen Safari-Builds keine QuotaExceededError
ausgegeben. Stattdessen werden Frames mit einem zweistufigen Algorithmus entfernt. Der Vorgang wird beendet, wenn genügend Platz für die appendBuffer()
vorhanden ist. Zuerst werden Frames zwischen 0 und 30 Sekunden vor der aktuellen Zeit in 30-Sekunden-Blöcken freigegeben. Als Nächstes werden Frames in 30-Sekunden-Chunks von der Dauer rückwärts bis zu 30 Sekunden nach currentTime
freigegeben. Weitere Informationen finden Sie in einer Webkit-Änderungsliste aus dem Jahr 2014.
Glücklicherweise wird dieser Fehler nicht nur in Chrome, sondern auch in Edge und Firefox ausgegeben. Wenn Sie einen anderen Browser verwenden, müssen Sie die Tests selbst durchführen. Der Test zum Grenzwert des Quellpuffers von François Beaufort ist zwar nicht das, was Sie für einen echten Mediaplayer entwickeln würden, aber Sie können damit zumindest das Verhalten beobachten.
Wie viele Daten kann ich anhängen?
Die genaue Anzahl variiert je nach Browser. Da Sie nicht nach der Menge der derzeit angehängten Daten fragen können, müssen Sie selbst nachverfolgen, wie viel Sie anhängen. Was du dir ansehen solltest, kannst du anhand der folgenden Daten entscheiden, die ich zum Zeitpunkt der Erstellung dieses Artikels zusammengetragen habe. Bei Chrome sind diese Zahlen Obergrenzen. Sie können also kleiner sein, wenn das System unter Speichermangel leidet.
Chrome | Chromecast* | Firefox | Safari | Edge | |
---|---|---|---|---|---|
Video | 150 MB | 30 MB | 100 MB | 290 MB | Unbekannt |
Audio | 12 MB | 2 MB | 15 MB | 14 MB | Unbekannt |
- oder ein anderes Chrome-Gerät mit begrenztem Arbeitsspeicher
Was kann ich tun?
Da die Anzahl der unterstützten Daten so stark variiert und Sie die Datenmenge nicht in einer SourceBuffer
finden, müssen Sie sie indirekt über die QuotaExceededError
abrufen. Sehen wir uns nun einige Möglichkeiten an.
Es gibt mehrere Ansätze, wie Sie mit QuotaExceededError
umgehen können. In der Praxis ist eine Kombination aus einem oder mehreren Ansätzen am besten. Du solltest die Arbeit darauf basieren, wie viel du abfragst und versuchst, über HTMLMediaElement.currentTime
hinaus anzuhängen, und diese Größe anhand der QuotaExceededError
anpassen. Mit einem Manifest wie einer mpd-Datei (MPEG-DASH) oder einer m3u8-Datei (HLS) kannst du die Daten im Auge behalten, die du dem Puffer hinzufügst.
Sehen wir uns nun einige Ansätze zur Behandlung der QuotaExceededError
an.
- Entfernen Sie nicht benötigte Daten und hängen Sie sie wieder an.
- Fügen Sie kleinere Fragmente an.
- Verringern Sie die Wiedergabeauflösung.
Sie können zwar kombiniert werden, ich werde sie aber einzeln behandeln.
Nicht benötigte Daten entfernen und wieder anhängen
Dieser Vorgang sollte eigentlich „Daten entfernen, die am wenigsten wahrscheinlich bald verwendet werden, und dann den Anhängen von Daten wiederholen, die wahrscheinlich bald verwendet werden“ heißen. Das war ein zu langer Titel. Sie müssen sich nur daran erinnern, was ich wirklich meine.
Das Entfernen aktueller Daten ist nicht so einfach wie das Aufrufen von SourceBuffer.remove()
. Wenn Sie Daten aus der SourceBuffer
entfernen möchten, muss das Aktualisierungsflag auf „false“ gesetzt sein. Andernfalls rufen Sie SourceBuffer.abort()
auf, bevor Sie Daten entfernen.
Beim Aufrufen von SourceBuffer.remove()
sind einige Dinge zu beachten.
- Das kann sich negativ auf die Wiedergabe auswirken. Wenn du beispielsweise möchtest, dass das Video bald wiederholt oder in einer Schleife abgespielt wird, solltest du den Anfang des Videos nicht entfernen. Wenn Sie oder der Nutzer zu einem Teil des Videos springen, aus dem Sie Daten entfernt haben, müssen Sie diese Daten noch einmal anhängen, um diese Suche zu erfüllen.
- Entfernen Sie diese Inhalte so vorsichtig wie möglich. Entfernen Sie die aktuell wiedergegebene Framegruppe, die beim Keyframe bei oder vor
currentTime
beginnt, da dies zu einer Verzögerung der Wiedergabe führen kann. Solche Informationen müssen möglicherweise von der Webanwendung aus dem Bytestream geparst werden, wenn sie nicht im Manifest verfügbar sind. Wenn Sie ein Medienmanifest oder eine App-Kenntnisse zu Keyframe-Intervallen in den Medien haben, können Sie die Entfernungsbereiche Ihrer App so festlegen, dass die aktuell wiedergegebenen Medien nicht entfernt werden. Entfernen Sie auf keinen Fall die aktuell abgespielte Gruppe von Bildern oder die ersten Bilder danach. Entfernen Sie das Medium im Allgemeinen nur dann, wenn Sie sicher sind, dass es nicht mehr benötigt wird. Wenn du das Video zu nah am Wiedergabepfeil entfernst, kann es zu Rucklern kommen. - In Safari 9 und Safari 10 wird
SourceBuffer.abort()
nicht korrekt implementiert. Stattdessen werden Fehler ausgegeben, die die Wiedergabe beenden. Glücklicherweise gibt es hier und hier offene Fehler-Tracker. In der Zwischenzeit müssen Sie das Problem irgendwie umgehen. Shaka Player macht das, indem in diesen Versionen von Safari eine leereabort()
-Funktion erstellt wird.
Kleinere Fragmente anhängen
Unten habe ich die Vorgehensweise dargestellt. Dies funktioniert möglicherweise nicht in jedem Fall, hat jedoch den Vorteil, dass die Größe der kleineren Blöcke an Ihre Anforderungen angepasst werden kann. Außerdem ist keine Rückkehr in das Netzwerk erforderlich, was für einige Nutzer zusätzliche Datenkosten verursachen kann.
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);
Wiedergabeauflösung verringern
Das ist vergleichbar mit dem Entfernen der letzten Daten und dem erneuten Anhängen. Sie können beides gleichzeitig tun, obwohl im Beispiel unten nur die Auflösung verringert wird.
Beachten Sie bei der Verwendung dieser Technik Folgendes:
- Sie müssen ein neues ‑Segment anhängen. Sie müssen dies jedes Mal tun, wenn Sie Darstellungen ändern. Das neue Initiationssegment muss für die nachfolgenden Mediensegmente sein.
- Der Präsentationszeitstempel der angehängten Medien sollte so genau wie möglich mit dem Zeitstempel der Daten im Zwischenspeicher übereinstimmen, darf aber nicht vorspringen. Das Überlappen der zwischengespeicherten Daten kann je nach Browser zu Rucklern oder kurzen Aussetzern führen. Unabhängig davon, was Sie anhängen, darf der Abspielkopf nicht überlappen, da dies zu Fehlern führt.
- Suchvorgänge können dazu führen, dass die Wiedergabe unterbrochen wird. Du könntest versucht sein, zu einer bestimmten Stelle zu springen und die Wiedergabe von dort fortzusetzen. Dadurch wird die Wiedergabe unterbrochen, bis die Suche abgeschlossen ist.