Start · Sprachen · JavaScript · Referenz · ManagedSourceBuffer: bufferedchange

ManagedSourceBuffer: bufferedchange

Ereignis

Wird ausgelöst, wenn sich der gepufferte Bereich (<code>buffered</code>) eines <code>ManagedSourceBuffer</code> ändert, auch wenn der Browser Daten selbst verwirft.

seit JavaScript Safari 17 (macOS/iPadOS), Safari 17.1 (iOS); in Ch Kategorie: webapi

Signatur

managedSourceBuffer.addEventListener("bufferedchange", handler)

Beschreibung

Das Ereignis bufferedchange gehört zur Schnittstelle ManagedSourceBuffer der Managed Media Source-API, einer speicherschonenden Variante von Media Source Extensions (MSE). Es wird ausgelöst, sobald sich die Zeitbereiche in sourceBuffer.buffered ändern. Der Handler bekommt ein BufferedChangeEvent mit den Eigenschaften addedRanges und removedRanges. Beide sind vom Typ TimeRanges.

Bei klassischem MSE ändert sich der Puffer nur durch eigene Aufrufe wie appendBuffer() oder remove(). Bei ManagedMediaSource darf der Browser dagegen jederzeit selbst gepufferte Daten entfernen, etwa bei Speicherknappheit. bufferedchange ist der einzige zuverlässige Weg, davon zu erfahren. Ein Player sollte das Ereignis deshalb nutzen, um entfernte Segmente in seiner Segmentverwaltung als „nicht geladen“ zu markieren und bei Bedarf neu zu laden.

Einen Handler registrierst du mit addEventListener("bufferedchange", handler) oder über die Eigenschaft onbufferedchange. Das Ereignis lässt sich nicht abbrechen und steigt nicht auf (kein Bubbling).

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "bufferedchange".
$handler Pflicht (event: BufferedChangeEvent) => void Callback-Funktion. Sie erhält ein BufferedChangeEvent mit addedRanges und removedRanges (jeweils TimeRanges).
$options boolean | AddEventListenerOptions false Optionen wie once oder signal (AbortSignal, damit lässt sich der Handler später gezielt entfernen).

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler bekommt ein BufferedChangeEvent.

Beispiele

Hinzugefügte und entfernte Bereiche protokollieren

const video = document.querySelector('video');
// Nötig, damit ManagedMediaSource ohne AirPlay-Alternative funktioniert
video.disableRemotePlayback = true;

const MS = window.ManagedMediaSource ?? window.MediaSource;
const source = new MS();
video.src = URL.createObjectURL(source);

const rangesToString = (ranges) =>
  Array.from({ length: ranges.length }, (_, i) =>
    `[${ranges.start(i).toFixed(2)}–${ranges.end(i).toFixed(2)}]`
  ).join(' ') || '(keine)';

source.addEventListener('sourceopen', async () => {
  const sb = source.addSourceBuffer('video/mp4; codecs="avc1.42E01E"');

  sb.addEventListener('bufferedchange', (event) => {
    console.log('Hinzugefügt:', rangesToString(event.addedRanges));
    console.log('Entfernt:', rangesToString(event.removedRanges));
  });

  const data = await fetch('/media/init-und-segment1.mp4').then((r) => r.arrayBuffer());
  sb.appendBuffer(data);
});
Hinzugefügt: [0.00–4.00] Entfernt: (keine)

Vom Browser verworfene Segmente neu laden

// segments: [{ start, end, url, loaded }]
const markEvicted = (segments, removedRanges) => {
  for (let i = 0; i < removedRanges.length; i++) {
    const from = removedRanges.start(i);
    const to = removedRanges.end(i);
    for (const seg of segments) {
      if (seg.start < to && seg.end > from) seg.loaded = false;
    }
  }
};

const watchBuffer = (sourceBuffer, segments) => {
  sourceBuffer.onbufferedchange = (event) => {
    if (event.removedRanges.length > 0) {
      markEvicted(segments, event.removedRanges);
      const missing = segments.filter((s) => !s.loaded).length;
      console.log(`Vom Browser entfernt – ${missing} Segment(e) neu laden`);
    }
  };
};
Vom Browser entfernt – 3 Segment(e) neu laden

Handler mit AbortController wieder entfernen

const controller = new AbortController();

const onBufferedChange = (event) => {
  console.log('Neue Bereiche:', event.addedRanges.length);
};

sourceBuffer.addEventListener('bufferedchange', onBufferedChange, {
  signal: controller.signal,
});

// Beim Zerstören des Players:
controller.abort();
// Alternativ:
// sourceBuffer.removeEventListener('bufferedchange', onBufferedChange);
// oder: sourceBuffer.onbufferedchange = null;
Neue Bereiche: 1

// Wichtig · Fallstricke

  • Kompatibilität: ManagedMediaSource und damit bufferedchange gibt es vor allem in Safari 17+, auf dem iPhone ab iOS 17.1. Andere Browser unterstützen die API nicht oder nur experimentell. Prüfe die Unterstützung mit 'ManagedMediaSource' in window und greife sonst auf MediaSource zurück. Beim klassischen SourceBuffer gibt es dieses Ereignis nicht.
  • Unter iOS funktioniert ManagedMediaSource nur, wenn am Medienelement disableRemotePlayback = true gesetzt ist oder eine AirPlay-taugliche Alternativquelle (z. B. HLS über <source>) angegeben ist.
  • Der Browser kann Daten ohne eigenen Aufruf entfernen. Verlass dich also nicht darauf, dass einmal angehängte Segmente erhalten bleiben. Werte removedRanges immer aus.
  • Das Ereignis kommt nach Ende eines appendBuffer()- oder remove()-Vorgangs, also in der Nähe von updateend. Die genaue Reihenfolge solltest du aber nicht voraussetzen. Stoße neue Anhänge nur an, wenn sourceBuffer.updating === false ist.
  • TimeRanges ist kein Array. Greife mit start(i)/end(i) und length auf die Bereiche zu.