Start · Sprachen · JavaScript · Referenz · MediaSource.addSourceBuffer()

MediaSource.addSourceBuffer()

Methode

Erstellt einen neuen <code>SourceBuffer</code> für den angegebenen MIME-Typ inklusive Codecs und fügt ihn der Liste <code>MediaSource.sourceBuffers</code> hinzu.

seit JavaScript Baseline: alle modernen Browser (iOS Safari erst a Kategorie: webapi

Signatur

MediaSource.prototype.addSourceBuffer(type)

Beschreibung

addSourceBuffer() ist bei den Media Source Extensions (MSE) der zentrale Schritt, bevor Mediendaten per JavaScript an ein <video>- oder <audio>-Element gegeben werden können. Die Methode erzeugt einen SourceBuffer, in den du anschließend mit appendBuffer() Segmente (z. B. fragmentiertes MP4 oder WebM) schreibst. So baust du adaptives Streaming (DASH, HLS über hls.js), Live-Streams oder eigene Player.

Aufrufen darfst du die Methode erst, wenn die MediaSource geöffnet ist, also readyState === "open" gilt. Das ist nach dem Ereignis sourceopen der Fall, das feuert, sobald die MediaSource über URL.createObjectURL() (oder srcObject in Workern) an ein Medienelement gebunden wurde.

Üblich ist ein Puffer pro Spur. Getrennte Audio- und Videospuren bekommen also jeweils einen eigenen SourceBuffer, gemultiplexte Streams nur einen. Wie viele Puffer gleichzeitig möglich sind, hängt vom Browser ab. Prüfe den MIME-Typ vorher mit MediaSource.isTypeSupported(), damit du keine Ausnahme bekommst.

Parameter

Name Typ Default Beschreibung
$type Pflicht string MIME-Typ der Mediendaten, üblicherweise mit Codec-Angabe, z. B. 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' oder 'video/webm; codecs="vp9, opus"'.

Rückgabewert

Typ
SourceBuffer
Beschreibung
Der neu erzeugte SourceBuffer. Er ist ab jetzt auch in mediaSource.sourceBuffers enthalten.

Beispiele

Grundaufbau: MediaSource an ein Video binden und SourceBuffer anlegen

const video = document.querySelector('video');
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

if ('MediaSource' in window && MediaSource.isTypeSupported(mime)) {
  const mediaSource = new MediaSource();
  video.src = URL.createObjectURL(mediaSource);

  mediaSource.addEventListener('sourceopen', async () => {
    URL.revokeObjectURL(video.src); // Objekt-URL wird nicht mehr gebraucht
    const sourceBuffer = mediaSource.addSourceBuffer(mime);

    const response = await fetch('/media/frag_bunny.mp4');
    const data = await response.arrayBuffer();

    sourceBuffer.addEventListener('updateend', () => {
      mediaSource.endOfStream();
      console.log('Puffer bereit, readyState:', mediaSource.readyState);
    }, { once: true });

    sourceBuffer.appendBuffer(data);
  }, { once: true });
} else {
  console.error('MIME-Typ oder MSE nicht unterstützt:', mime);
}
Puffer bereit, readyState: ended

Getrennte Audio- und Videospuren (DASH-typisch)

const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);

const appendAsync = (sb, buf) => new Promise((resolve, reject) => {
  sb.addEventListener('updateend', resolve, { once: true });
  sb.addEventListener('error', reject, { once: true });
  sb.appendBuffer(buf);
});

const load = async (url) => (await fetch(url)).arrayBuffer();

mediaSource.addEventListener('sourceopen', async () => {
  const videoSB = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001F"');
  const audioSB = mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
  console.log('Anzahl Puffer:', mediaSource.sourceBuffers.length);

  // Init-Segmente zuerst, dann Mediensegmente
  await Promise.all([
    appendAsync(videoSB, await load('/dash/video_init.mp4')),
    appendAsync(audioSB, await load('/dash/audio_init.mp4'))
  ]);
  await Promise.all([
    appendAsync(videoSB, await load('/dash/video_1.m4s')),
    appendAsync(audioSB, await load('/dash/audio_1.m4s'))
  ]);
  mediaSource.endOfStream();
}, { once: true });
Anzahl Puffer: 2

Fehlerbehandlung beim Anlegen

const tryAddSourceBuffer = (mediaSource, mime) => {
  try {
    return mediaSource.addSourceBuffer(mime);
  } catch (err) {
    switch (err.name) {
      case 'NotSupportedError':
        console.warn('Codec nicht unterstützt:', mime);
        break;
      case 'QuotaExceededError':
        console.warn('Keine weiteren SourceBuffer möglich');
        break;
      case 'InvalidStateError':
        console.warn('MediaSource ist nicht "open":', mediaSource.readyState);
        break;
      default:
        console.error(err);
    }
    return null;
  }
};

const ms = new MediaSource();
// Noch nicht an ein Element gebunden, also readyState "closed"
tryAddSourceBuffer(ms, 'video/webm; codecs="vp9"');
MediaSource ist nicht "open": closed

// Wichtig · Fallstricke

  • Ausnahmen: TypeError bei leerem type. NotSupportedError, wenn Typ oder Codec nicht unterstützt werden oder die Kombination mit den vorhandenen Puffern unzulässig ist. QuotaExceededError, wenn keine weiteren Puffer möglich sind. InvalidStateError, wenn readyState nicht "open" ist.
  • Zeitpunkt: Rufe die Methode erst im sourceopen-Handler auf. Gleich nach new MediaSource() scheitert der Aufruf.
  • Codec-Angabe: Ohne codecs-Parameter lehnen manche Browser (vor allem Safari) den Typ ab oder raten falsch. Gib die Codecs daher immer genau an und prüfe sie vorher mit MediaSource.isTypeSupported().
  • Container: MP4-Daten müssen fragmentiert sein (fMP4). Ein normales MP4 mit moov-Box am Ende funktioniert nicht.
  • Nebenläufigkeit: Solange sourceBuffer.updating true ist, darfst du nicht erneut appendBuffer() aufrufen. Warte auf updateend oder baue eine Warteschlange.
  • Kompatibilität: Auf dem iPhone gibt es MSE erst ab iOS 17.1, und zwar nur als ManagedMediaSource. Dessen addSourceBuffer() funktioniert gleich, zusätzlich braucht das Video-Element disableRemotePlayback oder eine AirPlay-Alternativquelle. In Dedicated Workern steht MediaSource in Chrome ab Version 108 zur Verfügung (Anbindung über MediaSourceHandle).