Start · Sprachen · JavaScript · Referenz · SourceBuffer.updating

SourceBuffer.updating

Eigenschaft

Schreibgeschützter boolescher Wert, der angibt, ob gerade ein <code>appendBuffer()</code>- oder <code>remove()</code>-Vorgang am <code>SourceBuffer</code> läuft.

seit JavaScript Baseline: alle modernen Browser (Media Source Exte Kategorie: webapi

Signatur

SourceBuffer.updating

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

Typ
boolean
Beschreibung
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 });
});
vorher: false direkt danach: true nach updateend: false

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);
  }
};
Stream beendet: ended

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 eine InvalidStateError-Ausnahme geworfen. Frage vorher immer updating ab, oder warte auf updateend.
  • updating wird synchron beim Aufruf von appendBuffer() bzw. remove() auf true gesetzt. Nutze zum Warten updateend und nicht update: updateend wird auch nach Fehlern und Abbrüchen ausgelöst.
  • Auch das Setzen von timestampOffset, mode oder appendWindowStart/appendWindowEnd sowie der Aufruf von MediaSource.endOfStream() und setLiveSeekableRange() scheitern, während ein Puffer updating === true meldet.
  • 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 ist updating wieder false. Ein laufendes remove() lässt sich so allerdings nicht abbrechen: Dann wirft abort() selbst einen InvalidStateError.
  • 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. Dessen SourceBuffer bietet ebenfalls updating. Die Eigenschaft ist auch in Dedicated Workers verfügbar, sofern MSE im Worker unterstützt wird.