Start · Sprachen · JavaScript · Referenz · SourceBuffer.timestampOffset

SourceBuffer.timestampOffset

Eigenschaft

Liest oder setzt den Versatz in Sekunden, der auf die Zeitstempel aller Mediensegmente addiert wird, die danach dem <code>SourceBuffer</code> hinzugefügt werden.

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

Signatur

SourceBuffer.timestampOffset

Beschreibung

Die Eigenschaft timestampOffset eines SourceBuffer legt fest, um wie viele Sekunden die Präsentationszeitstempel der danach per appendBuffer() eingefügten Mediensegmente verschoben werden. Ein positiver Wert schiebt Segmente auf der Zeitachse der Medienpräsentation nach hinten, ein negativer Wert nach vorn. Der Standardwert ist 0.

Gebraucht wird sie vor allem, wenn Segmente an einer anderen Stelle erscheinen sollen, als ihre eingebetteten Zeitstempel angeben. Typische Fälle:

  • Mehrere unabhängig kodierte Clips, die jeweils bei 0 beginnen, lückenlos hintereinander abspielen (etwa Werbeeinblendungen oder Playlisten).
  • Livestreams oder Segmente mit großen Start-Zeitstempeln auf 0 normalisieren.
  • Im Modus mode = "sequence" zu einer bestimmten Stelle springen. Dort passt der Browser timestampOffset nach jedem eingefügten Segment automatisch an.

Der Wert gilt immer nur für Daten, die nach dem Setzen eingefügt werden. Bereits gepufferte Daten bleiben unverändert. Man darf den Wert nur setzen, solange updating den Wert false hat und der Parser kein Mediensegment halb verarbeitet hat.

Parameter

Name Typ Default Beschreibung
$value number 0 Neuer Versatz in Sekunden (Gleitkommazahl, auch negativ). Wird beim Setzen der Eigenschaft zugewiesen.

Rückgabewert

Typ
number
Beschreibung
Der aktuelle Versatz in Sekunden (Gleitkommazahl), der auf die Zeitstempel neu eingefügter Mediensegmente addiert wird.

Beispiele

Zwei Clips lückenlos hintereinander puffern

const video = document.querySelector('video');
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);

// Wartet, bis der laufende Anfüge-Vorgang abgeschlossen ist
const waitForUpdate = (sb) =>
  new Promise((resolve) => sb.addEventListener('updateend', resolve, { once: true }));

mediaSource.addEventListener('sourceopen', async () => {
  const sb = mediaSource.addSourceBuffer(mime);
  // Beide Dateien sind fragmentiert (fMP4) und beginnen jeweils bei 0 s
  const clips = ['/media/intro.mp4', '/media/main.mp4'];

  for (const url of clips) {
    const data = await (await fetch(url)).arrayBuffer();
    // Neuen Clip an das Ende des bisher Gepufferten hängen
    const end = sb.buffered.length ? sb.buffered.end(sb.buffered.length - 1) : 0;
    sb.timestampOffset = end;
    sb.appendBuffer(data);
    await waitForUpdate(sb);
    console.log(`Clip ${url} mit Versatz ${sb.timestampOffset.toFixed(2)} s eingefügt`);
  }
  mediaSource.endOfStream();
});
Clip /media/intro.mp4 mit Versatz 0.00 s eingefügt Clip /media/main.mp4 mit Versatz 12.48 s eingefügt

Livestream-Zeitstempel auf 0 normalisieren

// Das erste Segment eines Livestreams beginnt z. B. bei 3600 s.
// Mit negativem Versatz erscheint es bei 0 s in der Zeitleiste.
const normalizeLive = (sourceBuffer, firstSegmentStart) => {
  if (sourceBuffer.updating) {
    throw new Error('SourceBuffer ist gerade beschäftigt');
  }
  sourceBuffer.timestampOffset = -firstSegmentStart;
};

// Aufruf, bevor das erste Mediensegment eingefügt wird:
// normalizeLive(sb, 3600);
// sb.appendBuffer(segment);
console.log('Versatz gesetzt:', -3600);
Versatz gesetzt: -3600

Automatische Anpassung im Modus "sequence"

mediaSource.addEventListener('sourceopen', async () => {
  const sb = mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
  sb.mode = 'sequence'; // Segmente werden in Einfügereihenfolge aneinandergereiht

  const segment = await (await fetch('/audio/chunk-1.m4s')).arrayBuffer();
  console.log('vorher:', sb.timestampOffset);
  sb.appendBuffer(segment);
  sb.addEventListener('updateend', () => {
    // Der Browser hat den Versatz selbst angepasst
    console.log('nachher:', sb.timestampOffset);
  }, { once: true });
});
vorher: 0 nachher: -1.021 (abhängig von den Zeitstempeln im Segment)

// Wichtig · Fallstricke

  • InvalidStateError: Das Setzen wirft einen Fehler, wenn updating den Wert true hat, der SourceBuffer aus mediaSource.sourceBuffers entfernt wurde oder der Parser gerade ein Mediensegment verarbeitet. Setze den Wert deshalb erst nach dem Ereignis updateend.
  • Steht MediaSource.readyState auf "ended", wechselt es beim Setzen zurück auf "open" (das Ereignis sourceopen wird erneut ausgelöst).
  • Im Modus "sequence" überschreibt der Browser den Wert nach jedem eingefügten Segment. Wer ihn manuell setzt, sollte ihn danach neu auslesen und nicht auf einen zwischengespeicherten Wert vertrauen.
  • Der Versatz wirkt nicht rückwirkend. Um bereits gepufferte Daten zu verschieben, entferne sie mit remove() und füge sie mit neuem Versatz erneut ein.
  • Bei fragmentiertem MP4 kann es durch Rundungen zwischen Zeitskalen zu Lücken oder Überlappungen im Millisekundenbereich kommen. Mit appendWindowStart und appendWindowEnd lassen sich unerwünschte Frames abschneiden.
  • Kompatibilität: wird von Chrome, Edge, Firefox und Safari (Desktop) unterstützt. Auf iOS steht MSE erst ab Safari 17.1 über ManagedMediaSource zur Verfügung, in Workern nur, wo MediaSource.canConstructInDedicatedWorker den Wert true hat.