Start · Sprachen · JavaScript · Referenz · SourceBuffer: update

SourceBuffer: update

Ereignis

Wird ausgelöst, wenn eine <code>appendBuffer()</code>- oder <code>remove()</code>-Operation auf einem <code>SourceBuffer</code> erfolgreich abgeschlossen wurde.

seit JavaScript Baseline: alle modernen Browser (Media Source Exte Kategorie: webapi

Signatur

sourceBuffer.addEventListener("update", handler)

Beschreibung

Das Ereignis update gehört zu den Media Source Extensions (MSE). Ein SourceBuffer löst es aus, sobald eine asynchrone Operation (appendBuffer() oder remove()) erfolgreich beendet ist. Zu diesem Zeitpunkt ist sourceBuffer.updating bereits wieder false, sodass man die nächste Operation starten darf.

Die Reihenfolge der Ereignisse ist festgelegt: updatestart → update → updateend. Schlägt eine Operation fehl, wird statt update das Ereignis error ausgelöst, bei einem Abbruch über abort() das Ereignis abort. updateend kommt in allen Fällen danach.

Einen Handler registriert man mit addEventListener("update", handler) oder über die Eigenschaft onupdate. Das übergebene Objekt ist ein einfaches Event ohne zusätzliche Eigenschaften. Es blubbert nicht (bubbles) und lässt sich nicht abbrechen.

In der Praxis steuert man damit oft eine Warteschlange von Mediensegmenten: Man hängt immer nur ein Segment an und reicht das nächste erst weiter, wenn das vorherige verarbeitet wurde. Wer auch Fehlerfälle zuverlässig behandeln will, nimmt dafür meist updateend, weil update nur bei Erfolg feuert.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname, hier "update".
$handler Pflicht (event: Event) => void Callback, der nach erfolgreichem Abschluss von appendBuffer() bzw. remove() aufgerufen wird. event.target ist der betroffene SourceBuffer.
$options boolean | AddEventListenerOptions false Optionen wie once, signal (zum Entfernen über AbortController) oder capture.

Rückgabewert

Typ
void
Beschreibung
addEventListener() gibt nichts zurück. Der Handler bekommt ein Event-Objekt.

Beispiele

Segmente nacheinander mit addEventListener anhängen

const video = document.querySelector('video');
const mimeType = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
const segmentUrls = ['/media/init.mp4', '/media/seg1.m4s', '/media/seg2.m4s'];

if (!MediaSource.isTypeSupported(mimeType)) {
  throw new Error('Codec wird nicht unterstützt');
}

const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);

mediaSource.addEventListener('sourceopen', async () => {
  const sourceBuffer = mediaSource.addSourceBuffer(mimeType);
  const segments = await Promise.all(
    segmentUrls.map(async (url) => (await fetch(url)).arrayBuffer())
  );

  let index = 0;
  const appendNext = () => {
    if (index < segments.length) {
      sourceBuffer.appendBuffer(segments[index++]);
    } else if (mediaSource.readyState === 'open') {
      mediaSource.endOfStream();
    }
  };

  sourceBuffer.addEventListener('update', () => {
    console.log(`Segment ${index} angehängt, updating = ${sourceBuffer.updating}`);
    appendNext();
  });

  appendNext();
}, { once: true });
Segment 1 angehängt, updating = false Segment 2 angehängt, updating = false Segment 3 angehängt, updating = false

on-Property onupdate und Handler wieder entfernen

const logBuffered = (event) => {
  const sb = event.target;
  const ranges = [];
  for (let i = 0; i < sb.buffered.length; i++) {
    ranges.push(`${sb.buffered.start(i).toFixed(1)}–${sb.buffered.end(i).toFixed(1)} s`);
  }
  console.log('Gepuffert:', ranges.join(', '));
};

// Variante 1: on-Property
sourceBuffer.onupdate = logBuffered;
// ... später deaktivieren
sourceBuffer.onupdate = null;

// Variante 2: addEventListener / removeEventListener
sourceBuffer.addEventListener('update', logBuffered);
sourceBuffer.removeEventListener('update', logBuffered);
Gepuffert: 0.0–12.0 s

remove() als Promise mit update/error verpacken

const removeRange = (sourceBuffer, start, end) =>
  new Promise((resolve, reject) => {
    const controller = new AbortController();
    const { signal } = controller;

    sourceBuffer.addEventListener('update', () => {
      controller.abort();
      resolve();
    }, { signal });

    sourceBuffer.addEventListener('error', () => {
      controller.abort();
      reject(new Error('remove() fehlgeschlagen'));
    }, { signal });

    sourceBuffer.remove(start, end);
  });

// Alten Puffer vor der aktuellen Position freigeben
const trimBuffer = async (video, sourceBuffer) => {
  const cutoff = video.currentTime - 30;
  if (cutoff > 0 && !sourceBuffer.updating) {
    await removeRange(sourceBuffer, 0, cutoff);
    console.log(`Puffer bis ${cutoff.toFixed(1)} s entfernt`);
  }
};
Puffer bis 45.3 s entfernt

// Wichtig · Fallstricke

  • Nur bei Erfolg: update kommt nicht, wenn die Operation fehlschlägt (error) oder abgebrochen wird (abort). Wer „fertig, egal wie“ erkennen will, nimmt updateend. Sonst kann eine Warteschlange hängen bleiben.
  • InvalidStateError: Wird appendBuffer() oder remove() aufgerufen, solange updating === true ist, gibt es eine Ausnahme. Deshalb immer erst auf update/updateend warten.
  • QuotaExceededError: Ist der Puffer voll, wirft appendBuffer() sofort eine Ausnahme und es kommt kein Ereignis. Dann zuerst alte Bereiche mit remove() freigeben.
  • endOfStream(): Darf nur aufgerufen werden, wenn kein SourceBuffer mehr updating ist, am besten also im update-/updateend-Handler.
  • Kompatibilität: MSE wird in Chrome, Edge, Firefox und Safari (Desktop) unterstützt. Auf dem iPhone gibt es MSE erst ab iOS 17.1, und zwar über ManagedMediaSource. Vorher vorhandene Unterstützung mit MediaSource.isTypeSupported() bzw. 'MediaSource' in window prüfen. In Dedicated Workers ist MSE nur teilweise verfügbar (Chrome 108+).