Signatur
Beschreibung
Die schreibgeschützte Eigenschaft buffered eines SourceBuffer sagt dir, welche Abschnitte der Medien-Zeitachse (in Sekunden) in diesem Puffer liegen und abgespielt werden können. Du bekommst ein TimeRanges-Objekt zurück. Es ist normalisiert: Die Bereiche sind aufsteigend sortiert, überlappen sich nicht und grenzen nicht direkt aneinander. Mit length, start(i) und end(i) liest du die einzelnen Bereiche aus.
Eigene Streaming-Player auf Basis der Media Source Extensions (MSE) nutzen diese Eigenschaft ständig, zum Beispiel für adaptives Streaming wie DASH oder HLS. Typische Aufgaben:
- Den Puffervorlauf vor der aktuellen Abspielposition messen, um zu entscheiden, ob das nächste Segment geladen werden muss.
- Alte Bereiche finden, die man mit
SourceBuffer.remove()freigeben kann, um Speicher zu sparen und einenQuotaExceededErrorzu vermeiden. - Lücken im Puffer erkennen, etwa nach einem Sprung (Seek) oder bei fehlerhaften Segmenten.
Achtung: Diese Bereiche gelten nur für diesen einen SourceBuffer. Wenn Audio und Video in getrennten Puffern liegen, kann das Medienelement nur dort abspielen, wo sich die Bereiche beider Puffer überschneiden. Diese Schnittmenge liefert HTMLMediaElement.buffered bzw. MediaSource automatisch.
Rückgabewert
TimeRanges-Objekt mit den gepufferten Bereichen in Sekunden. Es ist leer (length === 0), wenn noch nichts angehängt wurde.Beispiele
Alle gepufferten Bereiche ausgeben
const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
const sb = mediaSource.addSourceBuffer(mime);
const data = await (await fetch('/media/segment-0.mp4')).arrayBuffer();
sb.appendBuffer(data);
sb.addEventListener('updateend', () => {
const ranges = sb.buffered;
for (let i = 0; i < ranges.length; i++) {
console.log(`Bereich ${i}: ${ranges.start(i).toFixed(2)}s – ${ranges.end(i).toFixed(2)}s`);
}
}, { once: true });
});
Puffervorlauf vor der Abspielposition berechnen
// Gibt zurück, wie viele Sekunden ab currentTime lückenlos gepuffert sind
const bufferAhead = (sourceBuffer, currentTime) => {
const ranges = sourceBuffer.buffered;
for (let i = 0; i < ranges.length; i++) {
if (currentTime >= ranges.start(i) && currentTime <= ranges.end(i)) {
return ranges.end(i) - currentTime;
}
}
return 0; // currentTime liegt in einer Lücke
};
// Im Player-Loop: nächstes Segment nur laden, wenn weniger als 10 s gepuffert sind
const checkBuffer = () => {
const ahead = bufferAhead(sb, video.currentTime);
console.log(`Vorlauf: ${ahead.toFixed(1)} s`);
if (ahead < 10 && !sb.updating) {
loadNextSegment();
}
};
video.addEventListener('timeupdate', checkBuffer);
Alten Puffer freigeben, um Speicher zu sparen
// Alles entfernen, was mehr als 30 s hinter der Abspielposition liegt
const evictOldData = (sb, currentTime, keepBehind = 30) => {
if (sb.updating || sb.buffered.length === 0) return;
const start = sb.buffered.start(0);
const removeEnd = currentTime - keepBehind;
if (removeEnd > start) {
sb.remove(start, removeEnd);
console.log(`Entferne ${start.toFixed(1)}s – ${removeEnd.toFixed(1)}s`);
}
};
evictOldData(sb, video.currentTime);
// Wichtig · Fallstricke
- Nur eine Momentaufnahme: Das zurückgegebene
TimeRanges-Objekt aktualisiert sich nicht selbst. Liesbuffereddeshalb nach jedemupdateendoder Sprung (Seek) neu aus. - Ausnahme: Wurde der
SourceBufferschon aus seinerMediaSourceentfernt (removeSourceBuffer()), wirft der Zugriff einenInvalidStateError. - Während
updating === truekann der Wert noch den alten Stand zeigen. Werte ihn am besten erst imupdateend-Ereignis aus. - Kleine Lücken: Zeitstempel von Segmenten passen oft nicht exakt zueinander. Dann entstehen Mini-Lücken von wenigen Millisekunden, und die Wiedergabe kann hängen bleiben. Viele Player überspringen solche Lücken mit einer Toleranz, z. B. 0,1 s.
- Getrennte Puffer: Bei getrennten Audio- und Video-Puffern zählt für die Wiedergabe die Schnittmenge. Diese liefert
HTMLMediaElement.buffered. - Kompatibilität: MSE wird in allen modernen Desktop-Browsern unterstützt. Auf dem iPhone ist MSE erst ab iOS 17.1 als
ManagedMediaSourceverfügbar. Prüfe die Unterstützung vorher, z. B. mit'MediaSource' in windowundMediaSource.isTypeSupported().