Start · Sprachen · JavaScript · Referenz · SourceBuffer.changeType()

SourceBuffer.changeType()

Methode

Wechselt den MIME-Typ (Container und Codec), den ein <code>SourceBuffer</code> für künftige <code>appendBuffer()</code>-Aufrufe erwartet, ohne dass ein neuer Puffer nötig ist.

seit JavaScript Media Source Extensions; Chrome 70+, Firefox 63+, Kategorie: webapi

Signatur

SourceBuffer.prototype.changeType(type)

Beschreibung

changeType() gehört zu den Media Source Extensions (MSE). Damit kann ein bestehender SourceBuffer mitten in der Wiedergabe auf ein anderes Containerformat oder einen anderen Codec umgestellt werden, etwa von H.264 in MP4 auf VP9 in WebM oder von AAC auf Opus. Ohne diese Methode müsste man die MediaSource neu aufbauen, und die Wiedergabe würde abreißen.

Typische Einsatzfälle sind adaptives Streaming (DASH/HLS-Player, die zwischen Darstellungen mit unterschiedlichen Codecs wechseln), das Einfügen von Werbung in einem anderen Format und Playlists mit gemischten Quellen. Nach dem Aufruf muss das nächste angehängte Segment ein Initialisierungssegment im neuen Format sein. Erst danach folgen die Mediensegmente.

Die Methode läuft synchron. Vorher darf der Puffer nicht gerade aktualisiert werden (updating === false), und der Typ sollte mit MediaSource.isTypeSupported() geprüft werden. Bereits gepufferte Daten bleiben erhalten. Wie der Übergang an den Segmentgrenzen aussieht, hängt vom mode ("segments" oder "sequence") und vom timestampOffset ab.

Parameter

Name Typ Default Beschreibung
$type Pflicht string MIME-Typ mit Codec-Angabe für die künftigen Daten, z. B. 'video/webm; codecs="vp9"' oder 'video/mp4; codecs="avc1.64001F"'. Leere Zeichenkette ist nicht erlaubt.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined).

Beispiele

Codec-Wechsel von MP4/H.264 auf WebM/VP9

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

// Wartet, bis der SourceBuffer mit dem aktuellen Update fertig ist
const waitForUpdate = (sb) =>
  new Promise((resolve) => sb.addEventListener('updateend', resolve, { once: true }));

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

mediaSource.addEventListener('sourceopen', async () => {
  const mp4Type = 'video/mp4; codecs="avc1.42E01E"';
  const webmType = 'video/webm; codecs="vp9"';
  const sb = mediaSource.addSourceBuffer(mp4Type);
  sb.mode = 'sequence'; // Zeitstempel nahtlos fortsetzen

  // Erster Teil: MP4
  sb.appendBuffer(await fetchSegment('/media/intro-init.mp4'));
  await waitForUpdate(sb);
  sb.appendBuffer(await fetchSegment('/media/intro-1.m4s'));
  await waitForUpdate(sb);

  // Wechsel auf WebM/VP9
  if (MediaSource.isTypeSupported(webmType)) {
    sb.changeType(webmType);
    // Zuerst muss das Initialisierungssegment des neuen Formats kommen
    sb.appendBuffer(await fetchSegment('/media/main-init.webm'));
    await waitForUpdate(sb);
    sb.appendBuffer(await fetchSegment('/media/main-1.webm'));
    await waitForUpdate(sb);
  }

  mediaSource.endOfStream();
});

Sicherer Wechsel mit Feature-Erkennung und Fehlerbehandlung

const switchCodec = async (sourceBuffer, newType) => {
  if (typeof sourceBuffer.changeType !== 'function') {
    throw new Error('changeType() wird nicht unterstützt');
  }
  if (!MediaSource.isTypeSupported(newType)) {
    throw new Error(`Typ nicht unterstützt: ${newType}`);
  }
  // Laufendes Update abwarten, sonst InvalidStateError
  if (sourceBuffer.updating) {
    await new Promise((r) => sourceBuffer.addEventListener('updateend', r, { once: true }));
  }
  try {
    sourceBuffer.changeType(newType);
    console.log(`Gewechselt auf ${newType}`);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

// Aufruf, z. B. im DASH-Player bei einem Wechsel der Darstellung
// await switchCodec(videoBuffer, 'video/mp4; codecs="av01.0.05M.08"');
Gewechselt auf video/mp4; codecs="av01.0.05M.08"

Audio-Wechsel von AAC auf Opus

const audioBuffer = mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');

// ... AAC-Segmente anhängen ...

audioBuffer.addEventListener('updateend', function onDone() {
  audioBuffer.removeEventListener('updateend', onDone);
  audioBuffer.changeType('audio/webm; codecs="opus"');
  console.log('Audio-Puffer erwartet jetzt Opus');
});
Audio-Puffer erwartet jetzt Opus

// Wichtig · Fallstricke

  • Ausnahmen: TypeError bei leerem type. InvalidStateError, wenn der SourceBuffer schon aus sourceBuffers entfernt wurde oder updating gerade true ist. NotSupportedError, wenn der Browser den Typ nicht unterstützt.
  • Ist die MediaSource im Zustand "ended", setzt der Aufruf sie wieder auf "open" und löst sourceopen aus.
  • Nach changeType() muss als Erstes ein Initialisierungssegment im neuen Format angehängt werden. Kommen gleich Mediensegmente, gibt es einen Dekodierfehler und die MediaSource wird mit "decode"-Fehler beendet.
  • Bei Formaten, die Zeitstempel erzeugen müssen (z. B. audio/mpeg, audio/aac), wird mode automatisch auf "sequence" gesetzt.
  • Kompatibilität: Läuft in allen aktuellen Desktop-Browsern. Auf iOS-iPhones gibt es MSE erst ab iOS 17.1 über ManagedMediaSource. Vorher per Feature-Erkennung ('changeType' in SourceBuffer.prototype) prüfen und im Zweifel die MediaSource neu anlegen.
  • Nahtlose Übergänge zwischen Codecs hängen vom Browser ab. Kurze Ruckler an der Wechselstelle kommen vor.