Signatur
Beschreibung
Die Eigenschaft updating des SourceBuffer-Interfaces aus den Media Source Extensions (MSE) ist true, solange ein asynchroner Vorgang läuft, der mit appendBuffer() oder remove() gestartet wurde. Wenn kein solcher Vorgang aktiv ist, ist sie false.
Der Wert ist wichtig, weil ein SourceBuffer immer nur einen Vorgang gleichzeitig verarbeiten kann. Wenn du appendBuffer(), remove() oder MediaSource.endOfStream() aufrufst oder Eigenschaften wie mode, timestampOffset, appendWindowStart bzw. appendWindowEnd setzt, während updating === true ist, wird eine InvalidStateError-DOMException geworfen.
Der Ablauf eines Vorgangs ist so: Beim Aufruf von appendBuffer() wird updating sofort (synchron) auf true gesetzt, und das Ereignis updatestart wird ausgelöst. Nach der Verarbeitung wird updating wieder false, dann folgen update und updateend. Bei einem Fehler kommen stattdessen error und dann updateend, bei einem Abbruch mit abort() kommen abort und dann updateend. In der Praxis schreibst du deshalb eine Warteschlange für Mediensegmente und arbeitest sie im updateend-Handler ab.
Rückgabewert
true, wenn gerade ein appendBuffer()- oder remove()-Vorgang läuft, sonst false.Beispiele
Zustand vor und nach appendBuffer() prüfen
const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
const sourceBuffer = mediaSource.addSourceBuffer(mime);
const response = await fetch('/media/init.mp4');
const data = await response.arrayBuffer();
console.log('vorher:', sourceBuffer.updating);
sourceBuffer.appendBuffer(data);
console.log('direkt danach:', sourceBuffer.updating);
sourceBuffer.addEventListener('updateend', () => {
console.log('nach updateend:', sourceBuffer.updating);
}, { once: true });
});
Warteschlange für Segmente mit updating und updateend
const createAppendQueue = (sourceBuffer) => {
const queue = [];
const processNext = () => {
// Nur anhängen, wenn der Puffer frei ist
if (sourceBuffer.updating || queue.length === 0) return;
sourceBuffer.appendBuffer(queue.shift());
};
sourceBuffer.addEventListener('updateend', processNext);
return {
push(segment) {
queue.push(segment);
processNext();
},
get pending() {
return queue.length;
}
};
};
// Nutzung innerhalb von 'sourceopen':
// const appendQueue = createAppendQueue(sourceBuffer);
// for (const url of segmentUrls) {
// const buf = await (await fetch(url)).arrayBuffer();
// appendQueue.push(buf);
// }
Stream erst beenden, wenn alle Puffer fertig sind
const waitForIdle = (sourceBuffer) =>
sourceBuffer.updating
? new Promise((resolve) =>
sourceBuffer.addEventListener('updateend', resolve, { once: true }))
: Promise.resolve();
const finishStream = async (mediaSource) => {
const buffers = Array.from(mediaSource.sourceBuffers);
await Promise.all(buffers.map(waitForIdle));
if (mediaSource.readyState === 'open') {
mediaSource.endOfStream();
console.log('Stream beendet:', mediaSource.readyState);
}
};
Alte Pufferbereiche sicher entfernen
const trimBuffer = (video, sourceBuffer, keepSeconds = 30) => {
if (sourceBuffer.updating || sourceBuffer.buffered.length === 0) {
return false; // später erneut versuchen
}
const start = sourceBuffer.buffered.start(0);
const removeEnd = video.currentTime - keepSeconds;
if (removeEnd > start) {
sourceBuffer.remove(start, removeEnd);
return true;
}
return false;
};
// Wichtig · Fallstricke
- Häufigster Fehler: Wenn du
appendBuffer()mehrmals direkt hintereinander aufrufst, wird eineInvalidStateError-Ausnahme geworfen. Frage vorher immerupdatingab, oder warte aufupdateend. updatingwird synchron beim Aufruf vonappendBuffer()bzw.remove()auftruegesetzt. Nutze zum Wartenupdateendund nichtupdate:updateendwird auch nach Fehlern und Abbrüchen ausgelöst.- Auch das Setzen von
timestampOffset,modeoderappendWindowStart/appendWindowEndsowie der Aufruf vonMediaSource.endOfStream()undsetLiveSeekableRange()scheitern, während ein Pufferupdating === truemeldet. - Die Eigenschaft ist schreibgeschützt. Eine Zuweisung wird ignoriert, im Strict Mode wirft sie einen
TypeError. - Mit
abort()kannst du einen laufenden Anhängevorgang abbrechen. Danach istupdatingwiederfalse. Ein laufendesremove()lässt sich so allerdings nicht abbrechen: Dann wirftabort()selbst einenInvalidStateError. - Kompatibilität: MSE wird von allen modernen Desktop-Browsern und von Android unterstützt. Auf dem iPhone gibt es MSE erst ab iOS 17.1, und zwar über
ManagedMediaSource. DessenSourceBufferbietet ebenfallsupdating. Die Eigenschaft ist auch in Dedicated Workers verfügbar, sofern MSE im Worker unterstützt wird.