Start · Sprachen · JavaScript · Referenz · MediaSource: sourceended

MediaSource: sourceended

Ereignis

Wird ausgelöst, wenn der <code>readyState</code> eines <code>MediaSource</code>-Objekts auf <code>"ended"</code> wechselt, typischerweise nach einem Aufruf von <code>endOfStream()</code>.

seit JavaScript Baseline: alle modernen Browser (iOS Safari erst a Kategorie: webapi

Signatur

mediaSource.addEventListener("sourceended", handler)

Beschreibung

Das Ereignis sourceended gehört zur Media Source Extensions API (MSE). Es wird an einem MediaSource-Objekt ausgelöst, sobald dessen readyState von "open" auf "ended" wechselt. Das geschieht, wenn die Anwendung MediaSource.prototype.endOfStream() aufruft. Damit teilt sie dem Browser mit, dass keine weiteren Mediendaten mehr angehängt werden.

Du kannst den Handler per addEventListener("sourceended", handler) oder über die Eigenschaft mediaSource.onsourceended registrieren. Das Ereignis ist ein einfaches Event ohne zusätzliche Eigenschaften. Es bubbelt nicht und lässt sich nicht abbrechen.

In der Praxis dient sourceended dazu, eigene Streaming-Player (Adaptive Streaming, DASH/HLS-Implementierungen) zu steuern: Oberflächenelemente aktualisieren, Download-Logik beenden oder Statistiken protokollieren. Wichtig ist, dass "ended" nicht endgültig ist. Ein erneuter Aufruf von SourceBuffer.appendBuffer() oder remove() setzt den Zustand wieder auf "open" und löst dabei sourceopen erneut aus.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignistyp, hier immer "sourceended".
$handler Pflicht (event: Event) => void Callback, der mit einem gewöhnlichen Event-Objekt aufgerufen wird; event.target ist die betroffene MediaSource.
$options boolean | AddEventListenerOptions false Optionale Listener-Optionen wie once oder signal.

Rückgabewert

Typ
void
Beschreibung
addEventListener() gibt nichts zurück. Der Handler erhält ein Event ohne zusätzliche Eigenschaften.

Beispiele

Stream abschließen und auf sourceended reagieren

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 sourceBuffer = mediaSource.addSourceBuffer(mime);
  const data = await (await fetch('/media/clip.mp4')).arrayBuffer();

  sourceBuffer.addEventListener('updateend', () => {
    // Alle Daten angehängt → Stream beenden
    if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
      mediaSource.endOfStream();
    }
  }, { once: true });

  sourceBuffer.appendBuffer(data);
});

mediaSource.addEventListener('sourceended', () => {
  console.log('Stream beendet, readyState:', mediaSource.readyState);
  console.log('Dauer:', mediaSource.duration.toFixed(2), 's');
});
Stream beendet, readyState: ended Dauer: 12.48 s

Handler über die on-Property setzen und wieder entfernen

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

mediaSource.onsourceended = (event) => {
  console.log('sourceended auf', event.target.constructor.name);
  document.querySelector('#status').textContent = 'Vollständig geladen';
  // Handler nur einmal benötigt
  mediaSource.onsourceended = null;
};
sourceended auf MediaSource

Alle Zustandswechsel protokollieren und Listener per AbortController entfernen

const mediaSource = new MediaSource();
const controller = new AbortController();
const log = (e) => console.log(`${e.type} → readyState=${mediaSource.readyState}`);

for (const type of ['sourceopen', 'sourceended', 'sourceclose']) {
  mediaSource.addEventListener(type, log, { signal: controller.signal });
}

const video = document.querySelector('video');
video.src = URL.createObjectURL(mediaSource);

// Später, z. B. beim Zerstören des Players:
// controller.abort(); // entfernt alle drei Listener
sourceopen → readyState=open sourceended → readyState=ended

// Wichtig · Fallstricke

  • endOfStream() darf nur aufgerufen werden, wenn readyState === "open" ist und kein SourceBuffer gerade aktualisiert (updating === false). Sonst wirft die Methode eine InvalidStateError-DOMException, und sourceended wird nicht ausgelöst.
  • Auch endOfStream("network") oder endOfStream("decode") lösen sourceended aus. Das Ereignis selbst unterscheidet aber nicht zwischen regulärem Ende und Fehler. Den Fehlerfall erkennst du zusätzlich am error-Ereignis des <video>-Elements.
  • Der Zustand "ended" lässt sich umkehren: appendBuffer() oder remove() öffnet die Quelle wieder und löst sourceopen erneut aus. Programmiere Handler daher so, dass sie mehrfach laufen dürfen (idempotent).
  • sourceended ist nicht dasselbe wie das ended-Ereignis des Media-Elements. Letzteres kommt erst, wenn die Wiedergabe tatsächlich das Ende erreicht hat.
  • Kompatibilität: Das Ereignis wird in Chrome, Edge, Firefox und Safari (Desktop) unterstützt. Auf dem iPhone steht MSE erst ab iOS 17.1 als ManagedMediaSource zur Verfügung. Prüfe die Unterstützung mit 'MediaSource' in window bzw. MediaSource.isTypeSupported().