Signatur
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
0beginnen, lückenlos hintereinander abspielen (etwa Werbeeinblendungen oder Playlisten). - Livestreams oder Segmente mit großen Start-Zeitstempeln auf
0normalisieren. - Im Modus
mode = "sequence"zu einer bestimmten Stelle springen. Dort passt der BrowsertimestampOffsetnach 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
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();
});
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);
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 });
});
// Wichtig · Fallstricke
- InvalidStateError: Das Setzen wirft einen Fehler, wenn
updatingden Werttruehat, derSourceBufferausmediaSource.sourceBuffersentfernt wurde oder der Parser gerade ein Mediensegment verarbeitet. Setze den Wert deshalb erst nach dem Ereignisupdateend. - Steht
MediaSource.readyStateauf"ended", wechselt es beim Setzen zurück auf"open"(das Ereignissourceopenwird 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
appendWindowStartundappendWindowEndlassen 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
ManagedMediaSourcezur Verfügung, in Workern nur, woMediaSource.canConstructInDedicatedWorkerden Werttruehat.