Start · Sprachen · JavaScript · Referenz · RTCRtpScriptTransformer.sendKeyFrameRequest()

RTCRtpScriptTransformer.sendKeyFrameRequest()

Methode

Fordert aus einem empfangsseitigen WebRTC Encoded Transform heraus beim entfernten Sender ein neues Schlüsselbild (Keyframe) an.

seit JavaScript Safari 15.4+, Firefox 117+; Chrome: nicht unterstü Kategorie: webapi

Signatur

RTCRtpScriptTransformer.prototype.sendKeyFrameRequest()

Beschreibung

Die Methode sendKeyFrameRequest() steht im Worker-Kontext auf einem RTCRtpScriptTransformer zur Verfügung. Diesen Transformer erhält man über das Event rtctransform, sobald auf der Hauptseite einem RTCRtpReceiver über receiver.transform = new RTCRtpScriptTransform(worker, options) ein Transform zugewiesen wurde. Wird sie aufgerufen, schickt der Browser eine RTCP-Nachricht (in der Regel Full Intra Request, FIR, bzw. Picture Loss Indication, PLI) an die Gegenstelle. Diese soll daraufhin so schnell wie möglich einen vollständigen Keyframe (I-Frame) senden.

Das braucht man vor allem dann, wenn der Empfänger Videoframes nicht mehr dekodieren kann. Typische Fälle: Bei Ende-zu-Ende-Verschlüsselung (z. B. SFrame oder ein eigenes Verfahren) wurde der Schlüssel gewechselt, und Delta-Frames mit dem alten Schlüssel lassen sich nicht mehr entschlüsseln. Oder Frames sind verloren gegangen bzw. beschädigt, und ohne einen neuen Keyframe würde das Bild bis zum nächsten regulären Keyframe einfrieren.

Die Methode gibt ein Promise zurück. Es wird erfüllt, sobald die Anfrage abgeschickt wurde. Das heißt aber nicht, dass der Keyframe schon angekommen ist. Ob er da ist, erkennt man im Lese-Stream des Transformers an einem Frame mit frame.type === "key".

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Ein Promise, das mit undefined erfüllt wird, sobald die Keyframe-Anfrage abgeschickt wurde. Es wird mit einem InvalidStateError (DOMException) abgelehnt, wenn der Transformer nicht zu einem RTCRtpReceiver gehört oder der Empfänger keine Videodaten verarbeitet bzw. nicht (mehr) aktiv ist.

Beispiele

Transform am Empfänger registrieren (Hauptseite)

// main.js – dem Empfänger einen Worker-Transform zuweisen
const worker = new Worker('receiver-worker.js');

pc.addEventListener('track', (event) => {
  if (event.track.kind === 'video') {
    event.receiver.transform = new RTCRtpScriptTransform(worker, {
      side: 'receiver'
    });
  }
});

Keyframe nach Schlüsselwechsel anfordern (Worker)

// receiver-worker.js
let currentKeyId = 0;
let transformer = null;

self.addEventListener('rtctransform', async (event) => {
  transformer = event.transformer;
  const { readable, writable } = transformer;

  await readable
    .pipeThrough(new TransformStream({
      transform(frame, controller) {
        // Hier würde die Entschlüsselung stattfinden
        controller.enqueue(frame);
      }
    }))
    .pipeTo(writable);
});

// Die Hauptseite meldet per postMessage einen neuen Schlüssel
self.addEventListener('message', async ({ data }) => {
  if (data.type === 'newKey' && transformer) {
    currentKeyId = data.keyId;
    try {
      await transformer.sendKeyFrameRequest();
      console.log('Keyframe angefordert für Schlüssel', currentKeyId);
    } catch (err) {
      console.error('Anfrage fehlgeschlagen:', err.name);
    }
  }
});
Keyframe angefordert für Schlüssel 2

Bei nicht dekodierbaren Frames Keyframe anfordern (mit Drosselung)

// receiver-worker.js
self.addEventListener('rtctransform', async ({ transformer }) => {
  let waitingForKey = false;
  let lastRequest = 0;

  const requestKeyFrame = async () => {
    const now = performance.now();
    if (now - lastRequest < 1000) return; // höchstens 1× pro Sekunde
    lastRequest = now;
    await transformer.sendKeyFrameRequest().catch(console.warn);
  };

  const stream = new TransformStream({
    async transform(frame, controller) {
      if (waitingForKey && frame.type !== 'key') {
        await requestKeyFrame();
        return; // Delta-Frame verwerfen, bis ein Keyframe kommt
      }
      if (frame.type === 'key') waitingForKey = false;

      const ok = frame.data.byteLength > 0; // vereinfachte Prüfung
      if (!ok) {
        waitingForKey = true;
        await requestKeyFrame();
        return;
      }
      controller.enqueue(frame);
    }
  });

  await transformer.readable.pipeThrough(stream).pipeTo(transformer.writable);
});

// Wichtig · Fallstricke

  • Nur auf der Empfängerseite: Ist der Transformer an einen RTCRtpSender gebunden, wird das Promise mit InvalidStateError abgelehnt. Auf der Senderseite nutzt man stattdessen generateKeyFrame().
  • Nur für Video: Bei Audio-Empfängern ist die Methode sinnlos und wird abgelehnt.
  • Kein Garantieversprechen: Die Gegenstelle kann die Anfrage ignorieren, verzögert beantworten oder mehrere Anfragen zusammenfassen. Deshalb nicht blind auf einen Keyframe warten, sondern frame.type im Stream prüfen.
  • Anfragen drosseln: Zu viele FIR/PLI-Anfragen erhöhen Bitrate und CPU-Last beim Sender, und Keyframes sind deutlich größer als Delta-Frames. Anfragen deshalb zeitlich begrenzen (Throttling).
  • Kompatibilität: Verfügbar ist die Methode nur im Worker über RTCRtpScriptTransform, und zwar in Safari und Firefox. Chromium-Browser implementieren bislang nur die ältere, nicht standardisierte API createEncodedStreams(), die diese Methode nicht kennt. Vor der Nutzung per Feature-Erkennung prüfen, z. B. mit 'RTCRtpScriptTransform' in window.