Signatur
Beschreibung
appendBuffer() ist die zentrale Methode der Media Source Extensions (MSE). Damit übergibt man dem Browser rohe Mediadaten, etwa ein Initialisierungssegment oder fragmentierte MP4- bzw. WebM-Mediensegmente. Diese Daten wurden vorher zum Beispiel per fetch() oder über einen WebSocket geladen. So bauen Player wie hls.js, dash.js oder Shaka Player adaptives Streaming (HLS/DASH), Live-Streams oder eigene Pufferstrategien.
Die Methode arbeitet asynchron: Nach dem Aufruf steht SourceBuffer.updating auf true. Nacheinander werden die Ereignisse updatestart, update und updateend ausgelöst. Tritt ein Fehler auf, kommen stattdessen error und dann updateend. Solange updating auf true steht, darf man kein weiteres appendBuffer() oder remove() aufrufen. Deshalb verwaltet man die Segmente in der Praxis in einer Warteschlange und hängt das nächste erst im updateend-Handler an.
Zuerst muss ein Initialisierungssegment angehängt werden (bei fMP4 die Boxen ftyp und moov), danach folgen die Mediensegmente. Die Daten müssen zu dem MIME-Typ und Codec passen, die bei MediaSource.addSourceBuffer() angegeben wurden. Normale, nicht fragmentierte MP4-Dateien werden nicht unterstützt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $source Pflicht | ArrayBuffer | TypedArray | DataView | Die anzuhängenden Mediasegmentdaten, z. B. ein Uint8Array mit einem fMP4- oder WebM-Segment. Bei TypedArray und DataView wird nur der sichtbare Ausschnitt (byteOffset/byteLength) verwendet. |
Rückgabewert
undefined zurück. Ob das Anhängen fertig ist, erfährt man über das Ereignis updateend, Fehler über das Ereignis error.Beispiele
Initialisierungs- und Mediensegment nacheinander anhängen
const video = document.querySelector('video');
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
if (MediaSource.isTypeSupported(mime)) {
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
URL.revokeObjectURL(video.src);
const sb = mediaSource.addSourceBuffer(mime);
// Hilfsfunktion: appendBuffer als Promise
const append = (data) => new Promise((resolve, reject) => {
sb.addEventListener('updateend', resolve, { once: true });
sb.addEventListener('error', reject, { once: true });
sb.appendBuffer(data);
});
const init = await (await fetch('/media/init.mp4')).arrayBuffer();
await append(init);
const seg1 = await (await fetch('/media/seg-1.m4s')).arrayBuffer();
await append(seg1);
mediaSource.endOfStream();
console.log('Gepuffert bis', sb.buffered.end(0), 's');
});
}
Warteschlange für Segmente aus einem WebSocket
const mediaSource = new MediaSource();
document.querySelector('video').src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', () => {
const sb = mediaSource.addSourceBuffer('video/webm; codecs="vp9, opus"');
const queue = [];
const pump = () => {
if (!sb.updating && queue.length > 0) {
sb.appendBuffer(queue.shift());
}
};
sb.addEventListener('updateend', pump);
const ws = new WebSocket('wss://example.com/live');
ws.binaryType = 'arraybuffer';
ws.addEventListener('message', (event) => {
queue.push(new Uint8Array(event.data));
pump();
});
});
QuotaExceededError abfangen und alten Puffer freigeben
const safeAppend = async (sb, video, data) => {
try {
sb.appendBuffer(data);
} catch (err) {
if (err.name === 'QuotaExceededError') {
// Alles freigeben, was mehr als 30 s hinter der Abspielposition liegt
const removeEnd = Math.max(0, video.currentTime - 30);
if (removeEnd > 0) {
sb.remove(0, removeEnd);
await new Promise((r) => sb.addEventListener('updateend', r, { once: true }));
sb.appendBuffer(data); // erneut versuchen
}
} else {
throw err;
}
}
};
// Wichtig · Fallstricke
InvalidStateErrorwird geworfen, wennsb.updating === trueist, wenn derSourceBufferschon aus derMediaSourceentfernt wurde oder wenn beim Media-Element ein Fehler vorliegt. Deshalb Aufrufe immer serialisieren. StehtreadyStateauf"ended", setzt ein Aufruf dieMediaSourcewieder auf"open".QuotaExceededErrorwird geworfen, wenn der Puffer voll ist. Browser begrenzen ihn oft auf etwa 100–150 MB für Video. Mitremove()abgespielte Bereiche freigeben und den Aufruf erneut versuchen.- Sind die Daten ungültig oder passen sie nicht zum Codec, kommt es zu einem Parse-Fehler. Dann wird das Ereignis
errorausgelöst, und dieMediaSourcewird in der Regel mit einem Decode-Fehler beendet (video.error). - Der Browser kopiert die übergebenen Daten. Sie können danach also wiederverwendet werden. Beim Übertragen aus einem Worker ist ein
ArrayBufferaber möglicherweise schon detached. endOfStream()erst aufrufen, wenn bei keinemSourceBuffermehrupdatingauftruesteht.- Kompatibilität: MSE wird in Chrome, Edge, Firefox und Safari auf dem Desktop unterstützt. Auf dem iPhone gibt es erst ab iOS 17.1
ManagedMediaSource. In Dedicated Workers ist MSE erst ab Chrome 108 verfügbar (MediaSource.canConstructInDedicatedWorker). Vorher mitMediaSource.isTypeSupported()prüfen. Die ältere MethodeappendStream()ist veraltet.