Start · Sprachen · JavaScript · Referenz · VideoEncoder: dequeue

VideoEncoder: dequeue

Ereignis

Wird ausgelöst, sobald sich <code>VideoEncoder.encodeQueueSize</code> verringert hat, also der Encoder einen Frame aus seiner Warteschlange übernommen hat.

seit JavaScript WebCodecs: Chrome/Edge 106+, Safari 17+, Firefox 1 Kategorie: webapi

Signatur

videoEncoder.addEventListener("dequeue", handler)

Beschreibung

Das dequeue-Ereignis eines VideoEncoder signalisiert, dass die interne Warteschlange ausstehender Kodieraufträge kleiner geworden ist. Der Wert von encodeQueueSize ist also gesunken. Den Handler registrierst du entweder mit addEventListener("dequeue", handler) oder über die Eigenschaft ondequeue.

Wichtig ist das Ereignis vor allem für die Gegendrucksteuerung (Backpressure): Statt encodeQueueSize ständig in einer Schleife oder mit setTimeout abzufragen, wartet die Anwendung auf dequeue und reicht erst dann neue VideoFrame-Objekte per encode() nach. So wächst die Warteschlange nicht grenzenlos, der Speicherverbrauch bleibt niedrig und die Latenz bei Echtzeit-Anwendungen wie WebRTC-ähnlichem Streaming oder Bildschirmaufnahmen bleibt klein.

Das Ereignis ist ein einfaches Event ohne zusätzliche Eigenschaften und wird nicht weitergereicht (bubbling). Der Browser kann mehrere Verringerungen zu einem einzigen Ereignis zusammenfassen. Lies deshalb im Handler immer den aktuellen Wert von encodeQueueSize aus, anstatt pro Ereignis genau einen freien Platz anzunehmen.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignistyp, hier immer "dequeue".
$handler Pflicht (event: Event) => void Callback, der bei jeder Verringerung der Warteschlange aufgerufen wird. Er bekommt ein einfaches Event-Objekt, dessen target der VideoEncoder ist.
$options boolean | AddEventListenerOptions false Optionale Listener-Optionen wie once oder signal (zum Entfernen über einen AbortController).

Rückgabewert

Typ
void
Beschreibung
addEventListener hat keinen Rückgabewert. Der Handler selbst bekommt ein Event-Objekt.

Beispiele

Warteschlange mit addEventListener beobachten

const encoder = new VideoEncoder({
  output: (chunk) => console.log('Chunk:', chunk.type, chunk.byteLength),
  error: (e) => console.error('Encoder-Fehler:', e)
});

encoder.configure({ codec: 'vp8', width: 640, height: 480, bitrate: 1_000_000, framerate: 30 });

encoder.addEventListener('dequeue', () => {
  console.log('Warteschlange jetzt:', encoder.encodeQueueSize);
});

// Testframe aus einem Canvas erzeugen
const canvas = new OffscreenCanvas(640, 480);
canvas.getContext('2d').fillRect(0, 0, 640, 480);
const frame = new VideoFrame(canvas, { timestamp: 0 });
encoder.encode(frame, { keyFrame: true });
frame.close();
Warteschlange jetzt: 0 Chunk: key 1234

Backpressure: nur kodieren, wenn Platz in der Warteschlange ist

const MAX_QUEUE = 2;

const waitForSpace = (encoder) =>
  new Promise((resolve) => {
    if (encoder.encodeQueueSize < MAX_QUEUE) return resolve();
    const onDequeue = () => {
      if (encoder.encodeQueueSize < MAX_QUEUE) {
        encoder.removeEventListener('dequeue', onDequeue);
        resolve();
      }
    };
    encoder.addEventListener('dequeue', onDequeue);
  });

const encodeStream = async (readable, encoder) => {
  const reader = readable.getReader();
  let count = 0;
  while (true) {
    const { value: frame, done } = await reader.read();
    if (done) break;
    await waitForSpace(encoder);
    encoder.encode(frame, { keyFrame: count++ % 60 === 0 });
    frame.close();
  }
  await encoder.flush();
};

// Nutzung, z. B. mit einer Kamera-Spur:
// const track = (await navigator.mediaDevices.getUserMedia({ video: true })).getVideoTracks()[0];
// const { readable } = new MediaStreamTrackProcessor({ track });
// await encodeStream(readable, encoder);

ondequeue-Eigenschaft setzen und wieder entfernen

const encoder = new VideoEncoder({
  output: () => {},
  error: console.error
});

encoder.ondequeue = () => {
  console.log('dequeue, Größe:', encoder.encodeQueueSize);
};

// Später Handler abmelden
encoder.ondequeue = null;

// Wichtig · Fallstricke

  • Ereignisse werden zusammengefasst: Nach einem dequeue können mehrere Plätze frei geworden sein. Werte deshalb immer encodeQueueSize aus und zähle nicht die Ereignisse.
  • dequeue bedeutet nicht, dass ein kodierter Chunk fertig ist. Fertige Daten kommen ausschließlich über den output-Callback.
  • Rufe frame.close() nach encode() auf. Sonst bleiben die GPU- und Speicherressourcen des Frames belegt, egal wie gut die Warteschlange gesteuert wird.
  • Nach reset() oder close() sinkt die Warteschlange auf 0. Wartende Promises wie im Backpressure-Beispiel sollten diesen Fall berücksichtigen, damit sie nicht ewig hängen bleiben.
  • Kompatibilität: WebCodecs gibt es nur in sicheren Kontexten (HTTPS). Das dequeue-Ereignis kam später dazu als die übrige API. Auf älteren Browsern bleibt nur das Abfragen von encodeQueueSize. Prüfe die Unterstützung mit 'ondequeue' in VideoEncoder.prototype.