Signatur
Beschreibung
Der Konstruktor MediaSource() gehört zu den Media Source Extensions (MSE). Er erzeugt ein neues MediaSource-Objekt ohne Quellpuffer. Mit diesem Objekt kannst du Mediendaten selbst in ein HTMLMediaElement einspeisen, statt einfach eine Datei-URL in src einzutragen. Darauf bauen adaptives Streaming (DASH, HLS über hls.js oder dash.js), Live-Streams und eigene Puffer-Strategien auf.
Nach dem Erzeugen ist readyState gleich "closed". Erst wenn du das Objekt an ein Medienelement bindest, wird es geöffnet. Dafür erzeugst du mit URL.createObjectURL(mediaSource) eine Objekt-URL und weist sie video.src zu. Alternativ setzt du in neueren Browsern video.srcObject auf das Objekt, das mediaSource.handle liefert. Danach wechselt readyState auf "open" und das Ereignis sourceopen wird ausgelöst.
Erst im sourceopen-Handler legst du mit addSourceBuffer(mimeType) einen oder mehrere SourceBuffer an. In diese hängst du dann mit appendBuffer() Mediensegmente an, z. B. fragmentiertes MP4 oder WebM. Wenn alle Daten angehängt sind, schließt endOfStream() den Stream ab.
Prüfe vor dem Erzeugen mit MediaSource.isTypeSupported(), ob der Browser das gewünschte Container- und Codec-Format unterstützt.
Rückgabewert
MediaSource-Objekt mit readyState === "closed", leerer sourceBuffers-Liste und duration gleich NaN.Beispiele
MediaSource erzeugen und an ein Video binden
const video = document.querySelector('video');
const mediaSource = new MediaSource();
console.log(mediaSource.readyState);
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', () => {
console.log(mediaSource.readyState);
// Objekt-URL wird nicht mehr gebraucht
URL.revokeObjectURL(video.src);
}, { once: true });
Fragmentiertes MP4 laden und abspielen
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
const video = document.querySelector('video');
if (!('MediaSource' in window) || !MediaSource.isTypeSupported(mime)) {
console.error('MSE oder Codec nicht unterstützt');
} else {
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
const sourceBuffer = mediaSource.addSourceBuffer(mime);
const response = await fetch('/media/film-fragmented.mp4');
const data = await response.arrayBuffer();
sourceBuffer.addEventListener('updateend', () => {
if (mediaSource.readyState === 'open') {
mediaSource.endOfStream();
}
video.play();
}, { once: true });
sourceBuffer.appendBuffer(data);
}, { once: true });
}
Segmente nacheinander anhängen (Warteschlange)
const mime = 'video/webm; codecs="vp9, opus"';
const segmente = ['/seg/init.webm', '/seg/1.webm', '/seg/2.webm'];
const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
const warteAufUpdate = (sb) =>
new Promise((resolve) => sb.addEventListener('updateend', resolve, { once: true }));
mediaSource.addEventListener('sourceopen', async () => {
const sb = mediaSource.addSourceBuffer(mime);
for (const url of segmente) {
const buf = await (await fetch(url)).arrayBuffer();
sb.appendBuffer(buf);
await warteAufUpdate(sb); // nie anhängen, solange sb.updating true ist
}
mediaSource.endOfStream();
console.log('Dauer:', mediaSource.duration);
}, { once: true });
// Wichtig · Fallstricke
- Kompatibilität: Chrome, Edge, Firefox und Safari auf dem Desktop unterstützen den Konstruktor seit Langem. Auf dem iPhone gibt es MSE erst ab iOS 17.1, und dort nur als
ManagedMediaSource. Prüfe deshalb beide Varianten:window.ManagedMediaSource || window.MediaSource. Wenn beide fehlen, kannst du bei HLS auf natives Abspielen zurückfallen. addSourceBuffer()zu früh aufgerufen: Vorsourceopenwirft der Aufruf einenInvalidStateError.- Ein Anhängevorgang nach dem anderen: Solange
sourceBuffer.updatingden Werttruehat, darfst du nicht erneutappendBuffer()aufrufen, sonst gibt es einenInvalidStateError. Warte aufupdateend. - Nur fragmentierte Formate: Normale MP4-Dateien mit
moov-Atom am Ende funktionieren nicht. Du brauchst fragmentiertes MP4 (fMP4), WebM oder MPEG-TS, sofern der Browser es unterstützt. - Speicher: Gib Objekt-URLs mit
URL.revokeObjectURL()frei. Entferne bei langen Streams alte Bereiche mitsourceBuffer.remove(), sonst wirftappendBuffer()irgendwann einenQuotaExceededError. - Dedicated Worker: Neuere Chromium-Versionen erlauben den Konstruktor auch in einem Worker. Das Objekt wird dann über
mediaSource.handleundpostMessage()übertragen.