Start · Sprachen · JavaScript · Referenz · MediaSource: sourceclose

MediaSource: sourceclose

Ereignis

Wird ausgelöst, wenn der <code>readyState</code> eines <code>MediaSource</code>-Objekts auf <code>"closed"</code> wechselt, die Quelle also nicht mehr an ein Medienelement gebunden ist.

seit JavaScript Baseline: alle modernen Browser (MSE); iOS Safari Kategorie: webapi

Signatur

mediaSource.addEventListener("sourceclose", handler)

Beschreibung

Das Ereignis sourceclose gehört zur Media Source Extensions API (MSE). Es tritt auf, sobald ein MediaSource-Objekt in den Zustand "closed" wechselt. Das passiert, wenn die Quelle von ihrem HTMLMediaElement getrennt wird, etwa weil video.src neu gesetzt oder entfernt wird, weil video.load() aufgerufen wird oder weil das Element eine andere Quelle lädt.

Wofür braucht man das? Vor allem zum Aufräumen: Timer für das Nachladen von Segmenten stoppen, laufende fetch-Anfragen per AbortController abbrechen, Objekt-URLs mit URL.revokeObjectURL() freigeben und interne Player-Zustände zurücksetzen. Ist die Quelle geschlossen, sind alle SourceBuffer aus mediaSource.sourceBuffers entfernt. Ein weiteres appendBuffer() wirft dann einen Fehler.

Registriert wird der Handler entweder mit addEventListener("sourceclose", handler) oder über die Eigenschaft onsourceclose. Das Ereignis ist ein einfaches Event ohne zusätzliche Eigenschaften. Es blubbert nicht (bubbles) und lässt sich nicht abbrechen. Den typischen Lebenszyklus bilden sourceopen → (sourceended) → sourceclose.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "sourceclose".
$handler Pflicht (event: Event) => void Callback, der ein einfaches Event erhält. event.target ist das betroffene MediaSource-Objekt.
$options boolean | AddEventListenerOptions false Optionen wie { once: true } oder { signal } zum späteren automatischen Entfernen.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Auch der Rückgabewert des Handlers wird ignoriert.

Beispiele

Aufräumen beim Schließen der Quelle

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

const controller = new AbortController();

mediaSource.addEventListener('sourceopen', async () => {
  const sb = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
  const res = await fetch('/media/init.mp4', { signal: controller.signal });
  sb.appendBuffer(await res.arrayBuffer());
}, { once: true });

mediaSource.addEventListener('sourceclose', () => {
  console.log('readyState:', mediaSource.readyState);
  controller.abort();          // laufende Downloads stoppen
  URL.revokeObjectURL(url);    // Speicher freigeben
});

// Später: Quelle wechseln, dadurch wird sourceclose ausgelöst
setTimeout(() => {
  video.removeAttribute('src');
  video.load();
}, 5000);
readyState: closed

Handler über onsourceclose setzen und wieder entfernen

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

mediaSource.onsourceclose = (event) => {
  console.log('Geschlossen:', event.type, event.target.sourceBuffers.length);
};

// Handler wieder entfernen
// mediaSource.onsourceclose = null;

mediaSource.addEventListener('sourceopen', () => {
  video.src = ''; // trennt die MediaSource, danach folgt sourceclose
}, { once: true });
Geschlossen: sourceclose 0

removeEventListener in einer Player-Klasse

class MsePlayer {
  #ms = new MediaSource();
  #timer = null;

  constructor(video) {
    this.video = video;
    this.onClose = this.onClose.bind(this);
    this.#ms.addEventListener('sourceclose', this.onClose);
    video.src = URL.createObjectURL(this.#ms);
    this.#timer = setInterval(() => console.log('Puffer prüfen ...'), 1000);
  }

  onClose() {
    clearInterval(this.#timer);
    this.#ms.removeEventListener('sourceclose', this.onClose);
    console.log('Player aufgeräumt');
  }

  destroy() {
    this.video.removeAttribute('src');
    this.video.load();
  }
}

const player = new MsePlayer(document.querySelector('video'));
setTimeout(() => player.destroy(), 3000);
Puffer prüfen ... Puffer prüfen ... Player aufgeräumt

// Wichtig · Fallstricke

  • Nicht mit sourceended verwechseln: endOfStream() setzt den Zustand auf "ended" und löst sourceended aus, aber nicht sourceclose. Geschlossen wird die Quelle erst beim Trennen vom Medienelement.
  • Nach sourceclose sind alle SourceBuffer entfernt. Aufrufe wie appendBuffer() oder endOfStream() werfen dann einen InvalidStateError. Asynchrone Lade-Schleifen sollten deshalb vor jedem Anhängen mediaSource.readyState prüfen.
  • Das Ereignis wird asynchron ausgelöst. Direkt nach video.src = '' ist readyState eventuell noch nicht "closed".
  • Eine geschlossene MediaSource lässt sich über eine neue Objekt-URL erneut anhängen. Meist ist ein neues Objekt aber sauberer.
  • Kompatibilität: MSE wird von allen Desktop-Browsern unterstützt. Auf dem iPhone steht MSE erst ab iOS 17.1 als ManagedMediaSource zur Verfügung. Vorher prüfen, zum Beispiel mit 'MediaSource' in window. In Dedicated Workern ist MediaSource nur in manchen Browsern verfügbar (Chrome 108+).