Start · Sprachen · JavaScript · Referenz · MediaSource

MediaSource

Schnittstelle

Das Interface <code>MediaSource</code> der Media Source Extensions (MSE) ist eine per JavaScript befüllbare Medienquelle für ein <code>HTMLMediaElement</code> wie <code>&lt;video&gt;</code> oder <code>&lt;audio&gt;</code>.

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

Signatur

interface MediaSource extends EventTarget

Beschreibung

MediaSource ist das zentrale Objekt der Media Source Extensions API. Statt einem <video>-Element einfach eine Datei-URL zu geben, erzeugt man eine MediaSource, verknüpft sie über URL.createObjectURL() (oder in neueren Browsern direkt über srcObject) mit dem Element und schiebt die Mediendaten selbst in Stücken hinein. Genau darauf bauen adaptive Streaming-Verfahren wie MPEG-DASH und HLS (z. B. in dash.js, hls.js oder Shaka Player) auf.

Die Mediendaten fließen nicht direkt in die MediaSource, sondern in einen oder mehrere SourceBuffer, die man mit addSourceBuffer(mimeType) anlegt – typischerweise einen für Video und einen für Audio. Die Segmente (meist fragmentiertes MP4 oder WebM) lädt man selbst per fetch() und übergibt sie mit SourceBuffer.appendBuffer().

Wichtige Bestandteile:

  • Konstruktor: new MediaSource() – hat keine Parameter.
  • Eigenschaften: readyState ("closed", "open", "ended"), duration, sourceBuffers, activeSourceBuffers, handle (nur in Workern).
  • Methoden: addSourceBuffer(), removeSourceBuffer(), endOfStream(error?), setLiveSeekableRange(), clearLiveSeekableRange().
  • Statische Methode: MediaSource.isTypeSupported(mimeType) prüft, ob ein Container-/Codec-Paar abgespielt werden kann.
  • Events: sourceopen, sourceended, sourceclose.

Der Ablauf ist immer gleich: MediaSource erzeugen, mit dem Medienelement verknüpfen, auf sourceopen warten, SourceBuffer anlegen, Segmente anhängen (dabei immer auf updateend warten) und am Schluss endOfStream() aufrufen.

Parameter

Name Typ Default Beschreibung
$mimeType Pflicht string Gilt für addSourceBuffer() und MediaSource.isTypeSupported(): MIME-Typ inklusive Codecs, z. B. 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'.
$error "network" | "decode" Gilt für endOfStream(error): Ohne Argument endet der Stream normal; mit "network" oder "decode" wird ein Fehler am Medienelement ausgelöst.
$sourceBuffer Pflicht SourceBuffer Gilt für removeSourceBuffer(): Der Puffer, der entfernt werden soll.
$start, end Pflicht number Gilt für setLiveSeekableRange(start, end): Bereich in Sekunden, in dem bei Live-Streams gesucht werden darf.

Rückgabewert

Typ
MediaSource
Beschreibung
new MediaSource() liefert eine neue Instanz mit readyState === "closed". Die Instanz wechselt erst nach der Verknüpfung mit einem Medienelement in den Zustand "open".

Beispiele

Grundgerüst: MediaSource mit einem Video verknüpfen

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

if ('MediaSource' in window && MediaSource.isTypeSupported(mime)) {
  const mediaSource = new MediaSource();
  console.log(mediaSource.readyState); // "closed"

  video.src = URL.createObjectURL(mediaSource);

  mediaSource.addEventListener('sourceopen', () => {
    URL.revokeObjectURL(video.src); // Object-URL wird nicht mehr benötigt
    console.log(mediaSource.readyState); // "open"
    const sourceBuffer = mediaSource.addSourceBuffer(mime);
    console.log(mediaSource.sourceBuffers.length);
  }, { once: true });
} else {
  console.error('MSE oder Codec nicht unterstützt');
}
closed open 1

Fragmentierte MP4-Segmente nacheinander anhängen

const video = document.querySelector('video');
const mime = 'video/mp4; codecs="avc1.64001F, mp4a.40.2"';
const segmente = ['/media/init.mp4', '/media/seg1.m4s', '/media/seg2.m4s'];

// Wartet, bis der SourceBuffer das Anhängen abgeschlossen hat
const appendAsync = (sb, data) => new Promise((resolve, reject) => {
  sb.addEventListener('updateend', resolve, { once: true });
  sb.addEventListener('error', reject, { once: true });
  sb.appendBuffer(data);
});

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

mediaSource.addEventListener('sourceopen', async () => {
  const sb = mediaSource.addSourceBuffer(mime);
  try {
    for (const url of segmente) {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      await appendAsync(sb, await res.arrayBuffer());
    }
    mediaSource.endOfStream();
    console.log('Dauer:', mediaSource.duration);
  } catch (err) {
    console.error(err);
    mediaSource.endOfStream('network');
  }
}, { once: true });
Dauer: 12.5 (abhängig vom Material)

Unterstützte Codecs ermitteln

const kandidaten = [
  'video/webm; codecs="vp9, opus"',
  'video/mp4; codecs="av01.0.05M.08"',
  'video/mp4; codecs="avc1.42E01E"',
  'video/mp4' // ohne Codec-Angabe oft zu ungenau
];

const MSE = window.ManagedMediaSource ?? window.MediaSource;
const unterstuetzt = kandidaten.filter((t) => MSE?.isTypeSupported(t));
console.log(unterstuetzt);
["video/webm; codecs=\"vp9, opus\"", "video/mp4; codecs=\"av01.0.05M.08\"", "video/mp4; codecs=\"avc1.42E01E\"", "video/mp4"] (browserabhängig)

Lebenszyklus-Events beobachten

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

const log = (e) => console.log(e.type, mediaSource.readyState);
mediaSource.addEventListener('sourceopen', log);
mediaSource.onsourceended = log;
mediaSource.onsourceclose = log;

video.src = URL.createObjectURL(mediaSource);

mediaSource.addEventListener('sourceopen', () => {
  mediaSource.duration = 60; // Dauer manuell festlegen
  mediaSource.endOfStream();
}, { once: true });

// Später: Quelle trennen
setTimeout(() => video.removeAttribute('src') || video.load(), 1000);
sourceopen open sourceended ended sourceclose closed