Signatur
Beschreibung
SourceBuffer gehört zu den Media Source Extensions (MSE). Mit dieser API kann JavaScript Audio- und Videodaten stückweise selbst in ein <video>- oder <audio>-Element einspeisen. Einen SourceBuffer baut man nicht direkt, sondern erzeugt ihn mit MediaSource.prototype.addSourceBuffer(mimeType). Jeder Puffer steht für einen Datenstrom mit einem bestimmten Container- und Codec-Format, z. B. video/mp4; codecs="avc1.42E01E".
Die wichtigste Methode ist appendBuffer(data). Sie hängt ein Segment als ArrayBuffer, TypedArray oder DataView an: zuerst ein Initialisierungssegment, danach Mediensegmente. Das Anhängen läuft asynchron. Solange updating den Wert true hat, darf keine weitere Operation starten. Das Ende meldet das Event updateend. Mit remove(start, end) löscht man bereits gepufferte Zeitbereiche, etwa um Speicher freizugeben. abort() bricht das aktuelle Segment ab und setzt den Parser zurück.
Wichtige Eigenschaften:
buffered(TimeRanges): die aktuell gepufferten Zeitbereiche.updating(boolean): zeigt an, ob gerade ein Append oder Remove läuft.mode("segments"|"sequence"): Beisegmentsbestimmen die Zeitstempel im Medium die Position. Beisequencewerden die Segmente einfach hintereinandergehängt.timestampOffset: ein Versatz, der auf die Zeitstempel neuer Segmente addiert wird.appendWindowStart/appendWindowEnd: Frames außerhalb dieses Zeitfensters werden verworfen.audioTracks,videoTracks,textTracks: die Spuren im Puffer (nicht in allen Browsern verfügbar).
Events: updatestart, update, updateend, error und abort. Man hört sie mit addEventListener ab oder setzt die passenden Eigenschaften (onupdateend usw.). Typische Einsatzgebiete sind adaptives Streaming (DASH, HLS über hls.js/dash.js), Live-Streams und Videos, die man selbst aus Fragmenten zusammensetzt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $data Pflicht | ArrayBuffer|TypedArray|DataView | Parameter von appendBuffer(data): das Init- oder Mediensegment, das angehängt werden soll. |
|
| $start Pflicht | number | Parameter von remove(start, end): Beginn des zu löschenden Bereichs in Sekunden. |
|
| $end Pflicht | number | Parameter von remove(start, end): Ende des Bereichs in Sekunden (Infinity ist erlaubt). |
|
| $type Pflicht | string | Parameter von changeType(type): neuer MIME-Typ inklusive Codecs, wenn Codec oder Container mitten im Stream wechseln. |
Rückgabewert
mediaSource.addSourceBuffer(mimeType). Die Methoden appendBuffer(), remove(), abort() und changeType() geben undefined zurück. Ob sie fertig sind, erfährt man über Events.Beispiele
SourceBuffer anlegen und Segmente abspielen
const video = document.querySelector('video');
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
if (!MediaSource.isTypeSupported(mime)) {
throw new Error(`Format nicht unterstützt: ${mime}`);
}
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
const sourceBuffer = mediaSource.addSourceBuffer(mime);
// Segment anhängen und auf updateend warten
const append = (data) => new Promise((resolve, reject) => {
sourceBuffer.addEventListener('updateend', resolve, { once: true });
sourceBuffer.addEventListener('error', reject, { once: true });
sourceBuffer.appendBuffer(data);
});
const urls = ['/media/init.mp4', '/media/seg1.m4s', '/media/seg2.m4s'];
for (const url of urls) {
const data = await (await fetch(url)).arrayBuffer();
await append(data);
}
mediaSource.endOfStream();
console.log('Gepuffert bis', sourceBuffer.buffered.end(0), 's');
}, { once: true });
Warteschlange für Appends (keine InvalidStateError)
class SegmentQueue {
#queue = [];
constructor(sourceBuffer) {
this.sb = sourceBuffer;
this.sb.addEventListener('updateend', () => this.#next());
}
push(data) {
this.#queue.push(data);
this.#next();
}
#next() {
if (this.sb.updating || this.#queue.length === 0) return;
this.sb.appendBuffer(this.#queue.shift());
}
}
// Nutzung: queue.push(segment) darf jederzeit aufgerufen werden
// const queue = new SegmentQueue(sourceBuffer);
Alten Puffer entfernen (Speicher sparen bei Live-Streams)
const video = document.querySelector('video');
const trimBuffer = (sourceBuffer, keepSeconds = 30) => {
if (sourceBuffer.updating || sourceBuffer.buffered.length === 0) return;
const start = sourceBuffer.buffered.start(0);
const cutoff = video.currentTime - keepSeconds;
if (cutoff > start) {
sourceBuffer.remove(start, cutoff);
}
};
// z. B. nach jedem erfolgreichen Append aufrufen:
// sourceBuffer.onupdateend = () => trimBuffer(sourceBuffer);
QuotaExceededError abfangen und Modus 'sequence' nutzen
const appendSafe = (sourceBuffer, data) => {
try {
sourceBuffer.appendBuffer(data);
} catch (err) {
if (err.name === 'QuotaExceededError') {
console.warn('Puffer voll – bitte erst alte Bereiche mit remove() löschen');
} else {
throw err;
}
}
};
// Segmente ohne passende Zeitstempel einfach hintereinanderhängen:
// sourceBuffer.mode = 'sequence';