Start · Sprachen · JavaScript · Referenz · VideoDecoder

VideoDecoder

Schnittstelle

Die WebCodecs-Schnittstelle <code>VideoDecoder</code> dekodiert kodierte Videostücke (<code>EncodedVideoChunk</code>) zu einzelnen Bildern (<code>VideoFrame</code>).

seit JavaScript Chrome/Edge 94+, Safari 16.4+, Firefox 130+ (nur s Kategorie: webapi

Signatur

interface VideoDecoder extends EventTarget

Beschreibung

VideoDecoder gehört zur WebCodecs API und bietet einen direkten, niedrigschwelligen Zugriff auf die Video-Decoder des Browsers, oft mit Hardwarebeschleunigung. Anders als <video> oder Media Source Extensions kümmert sich der Decoder nicht um Container-Formate (MP4, WebM), Pufferung oder Wiedergabe. Man übergibt ihm bereits aus dem Container gelöste Videostücke (EncodedVideoChunk) und bekommt dekodierte VideoFrame-Objekte zurück.

Der Ablauf ist immer gleich:

  • Erzeugen mit new VideoDecoder({ output, error }). Der Rückruf output erhält jedes fertige Bild, error meldet schwere Fehler.
  • Konfigurieren mit configure(config), z. B. mit codec: "avc1.42E01E", optional description (etwa avcC-Daten), codedWidth/codedHeight und hardwareAcceleration.
  • Füttern mit decode(chunk). Das erste Stück muss ein Schlüsselbild (type: "key") sein.
  • Leeren mit await flush(), danach close() bzw. reset().

Wichtige Mitglieder: die Eigenschaften state ("unconfigured", "configured" oder "closed") und decodeQueueSize (Anzahl der noch offenen Dekodieraufträge), das Ereignis dequeue (wenn die Warteschlange kürzer wird), die Methoden configure(), decode(), flush(), reset() und close() sowie die statische Methode VideoDecoder.isConfigSupported(config).

Typische Einsatzfälle sind Videoschnitt im Browser, Cloud-Gaming, eigene WebRTC- bzw. WebTransport-Streams, Bildanalyse Bild für Bild (z. B. mit WebGL/WebGPU oder Canvas) und das Erzeugen von Vorschaubildern.

Parameter

Name Typ Default Beschreibung
$init.output Pflicht (frame: VideoFrame) => void Wird für jedes dekodierte Bild aufgerufen. Das VideoFrame muss nach Gebrauch mit frame.close() freigegeben werden.
$init.error Pflicht (error: DOMException) => void Wird bei einem schweren Fehler aufgerufen. Der Decoder ist danach im Zustand "closed".
$config.codec Pflicht string Codec-Zeichenkette, z. B. "avc1.42E01E" (H.264), "vp8", "vp09.00.10.08", "av01.0.04M.08", "hev1...".
$config.description BufferSource Codec-spezifische Zusatzdaten, z. B. der avcC-Block bei H.264 im AVC-Format. Fehlt er, wird Annex-B erwartet.
$config.codedWidth / codedHeight number Kodierte Bildgröße in Pixeln. Hilft dem Decoder bei der Vorab-Reservierung.
$config.hardwareAcceleration "no-preference" | "prefer-hardware" | "prefer-software" "no-preference" Bevorzugte Art der Dekodierung.
$config.optimizeForLatency boolean false Gibt Bilder so früh wie möglich aus. Das ist für Echtzeit-Streams sinnvoll.

Rückgabewert

Typ
VideoDecoder
Beschreibung
Der Konstruktor liefert eine neue Instanz im Zustand "unconfigured". decode(), configure(), reset() und close() geben undefined zurück, flush() ein Promise<void> und isConfigSupported() ein Promise<VideoDecoderSupport>.

Beispiele

Unterstützung prüfen und Decoder anlegen

const config = {
  codec: 'vp8',
  codedWidth: 640,
  codedHeight: 480,
  hardwareAcceleration: 'prefer-hardware'
};

if (!('VideoDecoder' in window)) {
  console.log('WebCodecs wird nicht unterstützt');
} else {
  const { supported } = await VideoDecoder.isConfigSupported(config);
  console.log('VP8 unterstützt:', supported);

  if (supported) {
    const decoder = new VideoDecoder({
      output: (frame) => {
        console.log(`Bild ${frame.timestamp} µs, ${frame.displayWidth}x${frame.displayHeight}`);
        frame.close(); // Speicher freigeben!
      },
      error: (e) => console.error('Decoderfehler:', e.message)
    });
    decoder.configure(config);
    console.log(decoder.state);
  }
}
VP8 unterstützt: true configured

Videostücke dekodieren und auf ein Canvas zeichnen

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const decoder = new VideoDecoder({
  output: (frame) => {
    canvas.width = frame.displayWidth;
    canvas.height = frame.displayHeight;
    ctx.drawImage(frame, 0, 0);
    frame.close();
  },
  error: (e) => console.error(e)
});

// avcC-Daten und Samples stammen z. B. aus einem MP4-Demuxer (mp4box.js)
const decodeTrack = async (avcC, samples) => {
  decoder.configure({
    codec: 'avc1.64001f',
    description: avcC,
    optimizeForLatency: true
  });

  for (const s of samples) {
    decoder.decode(new EncodedVideoChunk({
      type: s.isKey ? 'key' : 'delta',
      timestamp: s.timestampUs,
      duration: s.durationUs,
      data: s.data
    }));
  }

  await decoder.flush(); // alle ausstehenden Bilder ausgeben
  console.log('Fertig, Warteschlange:', decoder.decodeQueueSize);
};
Fertig, Warteschlange: 0

Gegendruck über decodeQueueSize und das dequeue-Ereignis

const waitForQueue = (decoder, max = 10) =>
  decoder.decodeQueueSize < max
    ? Promise.resolve()
    : new Promise((resolve) =>
        decoder.addEventListener('dequeue', resolve, { once: true })
      );

const feed = async (decoder, chunks) => {
  for (const chunk of chunks) {
    await waitForQueue(decoder); // nicht zu viele Aufträge auf einmal
    decoder.decode(chunk);
  }
  await decoder.flush();
  decoder.close();
  console.log(decoder.state);
};

// Alternativ über die on-Property:
// decoder.ondequeue = () => console.log('Warteschlange:', decoder.decodeQueueSize);
closed