Signatur
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ückrufoutputerhält jedes fertige Bild,errormeldet schwere Fehler. - Konfigurieren mit
configure(config), z. B. mitcodec: "avc1.42E01E", optionaldescription(etwa avcC-Daten),codedWidth/codedHeightundhardwareAcceleration. - Füttern mit
decode(chunk). Das erste Stück muss ein Schlüsselbild (type: "key") sein. - Leeren mit
await flush(), danachclose()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
"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);
}
}
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);
};
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);