Signatur
Beschreibung
VideoEncoder gibt dir direkten Zugriff auf die Video-Encoder des Browsers, meist mit Hardware-Beschleunigung. Du übergibst rohe Einzelbilder (VideoFrame) und bekommst über einen Callback komprimierte Datenpakete (EncodedVideoChunk) zurück. Diese kannst du zum Beispiel in einen Container wie MP4 oder WebM schreiben (Muxing), über WebTransport oder WebSocket streamen oder weiterverarbeiten.
Die API arbeitet asynchron und zustandsbehaftet: Nach dem Konstruktor (new VideoEncoder({ output, error })) muss der Encoder mit configure() auf einen Codec, eine Auflösung und eine Bitrate eingestellt werden. Danach nimmt encode(frame, options) Frames in eine interne Warteschlange auf. Mit flush() wartest du, bis alle ausstehenden Frames kodiert sind, und mit close() gibst du die Ressourcen endgültig frei.
Die wichtigsten Mitglieder:
- Eigenschaften:
state("unconfigured","configured","closed") undencodeQueueSize(Anzahl wartender Kodieraufträge). - Methoden:
configure(config),encode(frame, options),flush(),reset(),close(). - Statisch:
VideoEncoder.isConfigSupported(config)prüft vorab, ob eine Konfiguration unterstützt wird. - Event:
dequeue(bzw.ondequeue) wird ausgelöst, wennencodeQueueSizekleiner wird. So lässt sich Gegendruck (Backpressure) steuern.
Einsatzgebiete sind Bildschirmaufnahmen im Browser, Videoschnitt im Web, Echtzeit-Streaming mit geringer Latenz und das Exportieren von Canvas- oder WebGL-Animationen als Video.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $init Pflicht | object | Initialisierungsobjekt für den Konstruktor new VideoEncoder(init). |
|
| $init.output Pflicht | (chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata) => void | Wird für jeden kodierten Chunk aufgerufen. metadata.decoderConfig enthält beim ersten Keyframe die Konfiguration, die ein VideoDecoder braucht (inklusive description bei AVC/HEVC). |
|
| $init.error Pflicht | (error: DOMException) => void | Wird bei einem schweren Fehler aufgerufen. Der Encoder wechselt danach in den Zustand "closed". |
Rückgabewert
VideoEncoder-Instanz im Zustand "unconfigured".Beispiele
Encoder anlegen, Konfiguration prüfen und einrichten
const config = {
codec: 'vp8',
width: 1280,
height: 720,
bitrate: 2_000_000, // 2 Mbit/s
framerate: 30,
};
const { supported } = await VideoEncoder.isConfigSupported(config);
if (!supported) throw new Error('Konfiguration nicht unterstützt');
const chunks = [];
const encoder = new VideoEncoder({
output: (chunk, metadata) => {
chunks.push(chunk);
if (metadata?.decoderConfig) {
console.log('Decoder-Config:', metadata.decoderConfig.codec);
}
},
error: (e) => console.error('Encoder-Fehler:', e.message),
});
encoder.configure(config);
console.log(encoder.state);
Canvas-Animation als Video kodieren (mit Keyframes)
const canvas = new OffscreenCanvas(640, 360);
const ctx = canvas.getContext('2d');
const chunks = [];
const encoder = new VideoEncoder({
output: (chunk) => chunks.push(chunk),
error: (e) => console.error(e),
});
encoder.configure({ codec: 'vp8', width: 640, height: 360, bitrate: 1_000_000, framerate: 30 });
const fps = 30;
for (let i = 0; i < 90; i++) {
ctx.fillStyle = `hsl(${i * 4}, 80%, 50%)`;
ctx.fillRect(0, 0, 640, 360);
const frame = new VideoFrame(canvas, { timestamp: (i * 1_000_000) / fps }); // Mikrosekunden
encoder.encode(frame, { keyFrame: i % 30 === 0 }); // jede Sekunde ein Keyframe
frame.close(); // Speicher sofort freigeben!
}
await encoder.flush();
encoder.close();
console.log(`${chunks.length} Chunks, erster ist ${chunks[0].type}`);
Kamera-Stream mit Backpressure über das dequeue-Event
const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 1280, height: 720 } });
const [track] = stream.getVideoTracks();
const reader = new MediaStreamTrackProcessor({ track }).readable.getReader();
const encoder = new VideoEncoder({
output: (chunk) => sendOverNetwork(chunk),
error: (e) => console.error(e),
});
encoder.configure({
codec: 'avc1.42001f', // H.264 Baseline, Level 3.1
width: 1280,
height: 720,
bitrate: 1_500_000,
latencyMode: 'realtime',
avc: { format: 'annexb' },
});
encoder.addEventListener('dequeue', () => {
console.log('Warteschlange:', encoder.encodeQueueSize);
});
function sendOverNetwork(chunk) {
const data = new Uint8Array(chunk.byteLength);
chunk.copyTo(data);
// z. B. über WebTransport senden …
}
let count = 0;
while (true) {
const { value: frame, done } = await reader.read();
if (done) break;
if (encoder.encodeQueueSize > 2) {
frame.close(); // Encoder überlastet: Frame verwerfen
continue;
}
encoder.encode(frame, { keyFrame: count++ % 150 === 0 });
frame.close();
}
// Wichtig · Fallstricke
- Nach
encode()immerframe.close()aufrufen.VideoFrames belegen oft GPU-Speicher. Wenn du sie nicht schließt, gerät der Browser schnell ins Stocken oder bricht mit einem Fehler ab.encode()legt eine eigene Kopie an, deshalb darfst du den Frame direkt danach schließen. - Zeitstempel sind in Mikrosekunden angegeben, nicht in Millisekunden.
- Der erste Frame muss ein Keyframe sein. Er wird automatisch als Keyframe kodiert. Für Streaming solltest du aber regelmäßig
{ keyFrame: true }setzen, damit Empfänger später einsteigen können. - Die Codec-Strings müssen vollständig sein, zum Beispiel
"avc1.42001f","vp09.00.10.08"oder"av01.0.04M.08". Ein einfaches"h264"funktioniert nicht. Prüfe Konfigurationen vorher mitVideoEncoder.isConfigSupported(), denn die Unterstützung hängt von Browser, Betriebssystem und Hardware ab. - Viele Encoder verlangen gerade Werte für Breite und Höhe, vor allem bei H.264 und 4:2:0-Farbunterabtastung.
- Nach einem Fehler im
error-Callback ist der Encoder geschlossen. Jeder weitere Aufruf wirft eineInvalidStateError. Mitreset()kannst du einen noch nicht geschlossenen Encoder zurücksetzen. Noch ausstehendeflush()-Promises werden dabei abgelehnt. - Achte auf
encodeQueueSizeund dasdequeue-Event. Wenn du ungebremst Frames einspeist, wächst der Speicherverbrauch immer weiter. - Die
output-Chunks sind reine Codec-Daten ohne Container. Für eine abspielbare MP4- oder WebM-Datei brauchst du einen Muxer, zum Beispiel die Bibliotheken mp4-muxer oder webm-muxer. - Kompatibilität: Die API ist nur in sicheren Kontexten (HTTPS) verfügbar, im Fenster und in Workern. Chromium unterstützt sie seit Version 94, Safari seit 16.4 und Firefox seit 130 (Desktop). Prüfe die Verfügbarkeit mit
'VideoEncoder' in globalThis. Für rechenintensive Aufgaben lohnt sich ein Dedicated Worker.