Signatur
Beschreibung
addSourceBuffer() ist bei den Media Source Extensions (MSE) der zentrale Schritt, bevor Mediendaten per JavaScript an ein <video>- oder <audio>-Element gegeben werden können. Die Methode erzeugt einen SourceBuffer, in den du anschließend mit appendBuffer() Segmente (z. B. fragmentiertes MP4 oder WebM) schreibst. So baust du adaptives Streaming (DASH, HLS über hls.js), Live-Streams oder eigene Player.
Aufrufen darfst du die Methode erst, wenn die MediaSource geöffnet ist, also readyState === "open" gilt. Das ist nach dem Ereignis sourceopen der Fall, das feuert, sobald die MediaSource über URL.createObjectURL() (oder srcObject in Workern) an ein Medienelement gebunden wurde.
Üblich ist ein Puffer pro Spur. Getrennte Audio- und Videospuren bekommen also jeweils einen eigenen SourceBuffer, gemultiplexte Streams nur einen. Wie viele Puffer gleichzeitig möglich sind, hängt vom Browser ab. Prüfe den MIME-Typ vorher mit MediaSource.isTypeSupported(), damit du keine Ausnahme bekommst.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | MIME-Typ der Mediendaten, üblicherweise mit Codec-Angabe, z. B. 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' oder 'video/webm; codecs="vp9, opus"'. |
Rückgabewert
SourceBuffer. Er ist ab jetzt auch in mediaSource.sourceBuffers enthalten.Beispiele
Grundaufbau: MediaSource an ein Video binden und SourceBuffer anlegen
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();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
URL.revokeObjectURL(video.src); // Objekt-URL wird nicht mehr gebraucht
const sourceBuffer = mediaSource.addSourceBuffer(mime);
const response = await fetch('/media/frag_bunny.mp4');
const data = await response.arrayBuffer();
sourceBuffer.addEventListener('updateend', () => {
mediaSource.endOfStream();
console.log('Puffer bereit, readyState:', mediaSource.readyState);
}, { once: true });
sourceBuffer.appendBuffer(data);
}, { once: true });
} else {
console.error('MIME-Typ oder MSE nicht unterstützt:', mime);
}
Getrennte Audio- und Videospuren (DASH-typisch)
const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
const appendAsync = (sb, buf) => new Promise((resolve, reject) => {
sb.addEventListener('updateend', resolve, { once: true });
sb.addEventListener('error', reject, { once: true });
sb.appendBuffer(buf);
});
const load = async (url) => (await fetch(url)).arrayBuffer();
mediaSource.addEventListener('sourceopen', async () => {
const videoSB = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001F"');
const audioSB = mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
console.log('Anzahl Puffer:', mediaSource.sourceBuffers.length);
// Init-Segmente zuerst, dann Mediensegmente
await Promise.all([
appendAsync(videoSB, await load('/dash/video_init.mp4')),
appendAsync(audioSB, await load('/dash/audio_init.mp4'))
]);
await Promise.all([
appendAsync(videoSB, await load('/dash/video_1.m4s')),
appendAsync(audioSB, await load('/dash/audio_1.m4s'))
]);
mediaSource.endOfStream();
}, { once: true });
Fehlerbehandlung beim Anlegen
const tryAddSourceBuffer = (mediaSource, mime) => {
try {
return mediaSource.addSourceBuffer(mime);
} catch (err) {
switch (err.name) {
case 'NotSupportedError':
console.warn('Codec nicht unterstützt:', mime);
break;
case 'QuotaExceededError':
console.warn('Keine weiteren SourceBuffer möglich');
break;
case 'InvalidStateError':
console.warn('MediaSource ist nicht "open":', mediaSource.readyState);
break;
default:
console.error(err);
}
return null;
}
};
const ms = new MediaSource();
// Noch nicht an ein Element gebunden, also readyState "closed"
tryAddSourceBuffer(ms, 'video/webm; codecs="vp9"');
// Wichtig · Fallstricke
- Ausnahmen:
TypeErrorbei leeremtype.NotSupportedError, wenn Typ oder Codec nicht unterstützt werden oder die Kombination mit den vorhandenen Puffern unzulässig ist.QuotaExceededError, wenn keine weiteren Puffer möglich sind.InvalidStateError, wennreadyStatenicht"open"ist. - Zeitpunkt: Rufe die Methode erst im
sourceopen-Handler auf. Gleich nachnew MediaSource()scheitert der Aufruf. - Codec-Angabe: Ohne
codecs-Parameter lehnen manche Browser (vor allem Safari) den Typ ab oder raten falsch. Gib die Codecs daher immer genau an und prüfe sie vorher mitMediaSource.isTypeSupported(). - Container: MP4-Daten müssen fragmentiert sein (fMP4). Ein normales MP4 mit
moov-Box am Ende funktioniert nicht. - Nebenläufigkeit: Solange
sourceBuffer.updatingtrueist, darfst du nicht erneutappendBuffer()aufrufen. Warte aufupdateendoder baue eine Warteschlange. - Kompatibilität: Auf dem iPhone gibt es MSE erst ab iOS 17.1, und zwar nur als
ManagedMediaSource. DessenaddSourceBuffer()funktioniert gleich, zusätzlich braucht das Video-ElementdisableRemotePlaybackoder eine AirPlay-Alternativquelle. In Dedicated Workern steht MediaSource in Chrome ab Version 108 zur Verfügung (Anbindung überMediaSourceHandle).