Start · Sprachen · JavaScript · Referenz · SourceBuffer

SourceBuffer

Schnittstelle

Das <code>SourceBuffer</code>-Interface ist ein Puffer für Mediensegmente, die über ein <code>MediaSource</code>-Objekt an ein <code>HTMLMediaElement</code> gehen und dort abgespielt werden.

seit JavaScript Media Source Extensions; Baseline: alle modernen B Kategorie: webapi

Signatur

interface SourceBuffer extends EventTarget

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"): Bei segments bestimmen die Zeitstempel im Medium die Position. Bei sequence werden 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

Typ
SourceBuffer
Beschreibung
Eine Instanz entsteht über 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 });
Gepuffert bis 8.008 s

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';
Puffer voll – bitte erst alte Bereiche mit remove() löschen