Start · Sprachen · JavaScript · Referenz · MediaSource: sourceopen

MediaSource: sourceopen

Ereignis

Wird ausgelöst, wenn der <code>readyState</code> eines <code>MediaSource</code>-Objekts auf <code>"open"</code> wechselt und Daten angehängt werden können.

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

Signatur

mediaSource.addEventListener("sourceopen", handler)

Beschreibung

Das Ereignis sourceopen gehört zur Media Source Extensions API (MSE). Es wird ausgelöst, sobald ein MediaSource-Objekt an ein Medienelement (<video> oder <audio>) angehängt wurde und sein readyState von "closed" bzw. "ended" auf "open" wechselt. Das passiert typischerweise, nachdem man video.src = URL.createObjectURL(mediaSource) gesetzt hat, oder erneut, wenn nach endOfStream() wieder Daten angehängt werden.

Erst nach diesem Ereignis darf man mit MediaSource.addSourceBuffer() einen SourceBuffer anlegen und Mediensegmente per appendBuffer() einspeisen. Ein Aufruf von addSourceBuffer() vor dem Öffnen führt zu einer InvalidStateError-Ausnahme. sourceopen ist daher der Einstiegspunkt jeder eigenen Streaming-Logik, etwa für adaptives Streaming (DASH/HLS-Player), Live-Streams oder das stückweise Nachladen großer Videodateien.

Den Handler registriert man entweder mit addEventListener("sourceopen", handler) oder über die Eigenschaft onsourceopen. Das Ereignis ist ein einfaches Event ohne zusätzliche Eigenschaften; es bubbelt nicht und ist nicht abbrechbar.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "sourceopen".
$handler Pflicht (event: Event) => void Callback, das ein einfaches Event erhält. event.target ist das auslösende MediaSource-Objekt.
$options boolean|AddEventListenerOptions false Optionen wie { once: true } – sinnvoll, da die Initialisierung meist nur einmal erfolgen soll.

Rückgabewert

Typ
void
Beschreibung
addEventListener() gibt undefined zurück. Der Handler erhält ein Event-Objekt.

Beispiele

Video-Datei stückweise über MSE abspielen

const video = document.querySelector('video');
const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
  const mediaSource = new MediaSource();

  mediaSource.addEventListener('sourceopen', async () => {
    console.log('readyState:', mediaSource.readyState);
    const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);

    const response = await fetch('/media/fragmentiert.mp4');
    const data = await response.arrayBuffer();

    sourceBuffer.addEventListener('updateend', () => {
      mediaSource.endOfStream();
      video.play();
    }, { once: true });

    sourceBuffer.appendBuffer(data);
  }, { once: true });

  video.src = URL.createObjectURL(mediaSource);
} else {
  console.error('Codec oder MSE nicht unterstützt');
}
readyState: open

Handler über die onsourceopen-Eigenschaft und Aufräumen der Objekt-URL

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

mediaSource.onsourceopen = (event) => {
  // Objekt-URL wird nach dem Anhängen nicht mehr benötigt
  URL.revokeObjectURL(url);
  console.log(event.type, event.target === mediaSource);
  // Handler entfernen, damit ein späteres erneutes Öffnen nichts doppelt anlegt
  mediaSource.onsourceopen = null;
};

video.src = url;
sourceopen true

Segmente nacheinander anhängen (einfacher Segment-Loader)

const video = document.querySelector('video');
const mediaSource = new MediaSource();
const segments = ['/seg/init.mp4', '/seg/1.m4s', '/seg/2.m4s', '/seg/3.m4s'];

const waitForUpdate = (sb) =>
  new Promise((resolve) => sb.addEventListener('updateend', resolve, { once: true }));

const onOpen = async () => {
  mediaSource.removeEventListener('sourceopen', onOpen);
  const sb = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001f"');

  for (const url of segments) {
    const chunk = await (await fetch(url)).arrayBuffer();
    sb.appendBuffer(chunk);
    await waitForUpdate(sb);
    console.log('angehängt:', url);
  }
  mediaSource.endOfStream();
};

mediaSource.addEventListener('sourceopen', onOpen);
video.src = URL.createObjectURL(mediaSource);
angehängt: /seg/init.mp4 angehängt: /seg/1.m4s angehängt: /seg/2.m4s angehängt: /seg/3.m4s

// Wichtig · Fallstricke

  • Reihenfolge beachten: Den Listener vor dem Setzen von video.src registrieren. Das Ereignis wird zwar asynchron ausgelöst, sauberer und robuster ist diese Reihenfolge aber trotzdem.
  • addSourceBuffer() vor sourceopen wirft InvalidStateError. Auch appendBuffer() darf erst aufgerufen werden, wenn sourceBuffer.updating den Wert false hat – sonst folgt ebenfalls InvalidStateError.
  • sourceopen kann mehrfach feuern: Nach endOfStream() (Zustand "ended") setzt ein erneutes appendBuffer() den Zustand wieder auf "open". Einmalige Initialisierung deshalb mit { once: true } absichern.
  • Gegenstücke: sourceended (Zustand "ended") und sourceclose (Objekt vom Medienelement getrennt).
  • Kompatibilität: In allen Desktop-Browsern und Android verfügbar. iOS Safari auf dem iPhone unterstützt klassisches MediaSource nicht; ab iOS 17.1 gibt es ManagedMediaSource mit demselben Ereignis. Vorher mit 'MediaSource' in window und MediaSource.isTypeSupported() prüfen.
  • In Dedicated Workers (Chrome 108+) kann MediaSource ebenfalls genutzt werden; dort wird der MediaSourceHandle statt einer Objekt-URL übergeben.