Signatur
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
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');
}
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 });
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);
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);