Start · Sprachen · JavaScript · Referenz · RTCRtpScriptTransform

RTCRtpScriptTransform

Schnittstelle

Fügt einen in einem Worker laufenden <code>TransformStream</code> (WebRTC Encoded Transform) in die Sende- oder Empfangs-Pipeline eines <code>RTCRtpSender</code>/<code>RTCRtpReceiver</code> ein.

seit JavaScript Firefox 117+, Safari 15.4+; Chromium: noch nicht v Kategorie: webapi

Signatur

interface RTCRtpScriptTransform

Beschreibung

RTCRtpScriptTransform gehört zur WebRTC-Erweiterung Encoded Transform. Damit greifen Sie auf bereits kodierte Audio- und Videoframes zu, also nachdem der Encoder gearbeitet hat und bevor das Paketieren beginnt (Sender), bzw. nach dem Entpaketieren und vor dem Decoder (Empfänger). Typische Anwendungen sind Ende-zu-Ende-Verschlüsselung (E2EE) über SFU-Server hinweg, das Anhängen von Metadaten an Frames oder Analyse und Statistik.

Das Objekt hat selbst keine öffentlichen Eigenschaften oder Methoden. Sie erzeugen es im Haupt-Thread mit new RTCRtpScriptTransform(worker, options, transfer) und weisen es der Eigenschaft transform eines RTCRtpSender oder RTCRtpReceiver zu. Im angegebenen Worker wird daraufhin das Ereignis rtctransform ausgelöst. Dessen Eigenschaft event.transformer ist ein RTCRtpScriptTransformer mit readable, writable und options.

Im Worker leiten Sie die Frames über readable.pipeThrough(new TransformStream(...)).pipeTo(writable) weiter und können sie dabei verändern. Weil die Verarbeitung im Worker stattfindet, bleibt der Haupt-Thread frei. Kodierte Frames sind RTCEncodedVideoFrame- bzw. RTCEncodedAudioFrame-Objekte, deren Nutzdaten als ArrayBuffer in frame.data liegen.

Parameter

Name Typ Default Beschreibung
$worker Pflicht Worker Dedizierter Worker, in dem das Ereignis rtctransform ausgelöst wird und die Transformation läuft.
$options any undefined Beliebiger strukturiert klonbarer Wert, z. B. { operation: 'encrypt' }. Im Worker steht er als event.transformer.options zur Verfügung.
$transfer Array<Transferable> [] Übertragbare Objekte, etwa ein MessagePort für den Schlüsselaustausch. Sie werden an den Worker übergeben und sind danach im Haupt-Thread nicht mehr nutzbar.

Beispiele

Transform im Haupt-Thread erzeugen und an Sender/Empfänger hängen

const worker = new Worker('e2ee-worker.js');
const pc = new RTCPeerConnection();

const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
for (const track of stream.getTracks()) {
  const sender = pc.addTrack(track, stream);
  sender.transform = new RTCRtpScriptTransform(worker, { operation: 'encrypt' });
}

pc.addEventListener('track', (event) => {
  event.receiver.transform = new RTCRtpScriptTransform(worker, { operation: 'decrypt' });
});

Worker: Frames per XOR „verschlüsseln“ (Demo, keine echte Kryptografie)

// e2ee-worker.js
const KEY = 0x5a;

const xorFrame = (frame, controller) => {
  const view = new Uint8Array(frame.data);
  for (let i = 0; i < view.length; i++) view[i] ^= KEY;
  frame.data = view.buffer;
  controller.enqueue(frame);
};

self.addEventListener('rtctransform', (event) => {
  const { readable, writable, options } = event.transformer;
  console.log('Transform gestartet:', options.operation);
  readable
    .pipeThrough(new TransformStream({ transform: xorFrame }))
    .pipeTo(writable)
    .catch((err) => console.error('Pipeline abgebrochen:', err));
});
Transform gestartet: encrypt Transform gestartet: decrypt

Schlüssel per MessagePort übertragen

// Haupt-Thread
const worker = new Worker('e2ee-worker.js');
const { port1, port2 } = new MessageChannel();

sender.transform = new RTCRtpScriptTransform(
  worker,
  { operation: 'encrypt', port: port2 },
  [port2]
);

// Schlüssel später rotieren
const key = crypto.getRandomValues(new Uint8Array(16));
port1.postMessage({ type: 'setKey', key });

// Im Worker:
// event.transformer.options.port.onmessage = ({ data }) => { currentKey = data.key; };

Feature-Erkennung mit Fallback für Chromium

const attachTransform = (sender, worker) => {
  if ('RTCRtpScriptTransform' in window) {
    sender.transform = new RTCRtpScriptTransform(worker, { operation: 'encrypt' });
    return 'script-transform';
  }
  if (typeof sender.createEncodedStreams === 'function') {
    // Ältere Chromium-API (PeerConnection mit encodedInsertableStreams: true nötig)
    const { readable, writable } = sender.createEncodedStreams();
    worker.postMessage({ operation: 'encrypt', readable, writable }, [readable, writable]);
    return 'insertable-streams';
  }
  return 'nicht unterstützt';
};

console.log(attachTransform(sender, worker));
script-transform

// Wichtig · Fallstricke

  • Browser-Unterstützung: Safari (ab 15.4) und Firefox (ab 117) unterstützen die API. Chromium-Browser bieten sie teilweise noch nicht an und nutzen stattdessen das nicht standardisierte createEncodedStreams(); dafür muss die RTCPeerConnection mit { encodedInsertableStreams: true } erzeugt werden. Prüfen Sie die Unterstützung immer per Feature-Erkennung.
  • Die Transformation läuft nur im Worker. Im Haupt-Thread gibt es kein RTCRtpScriptTransformer, und das Ereignis rtctransform wird ausschließlich im Worker-Scope (self) ausgelöst.
  • Jede Instanz darf nur einem Sender oder Empfänger zugewiesen werden. Eine zweite Zuweisung wirft einen InvalidStateError. Legen Sie deshalb pro Track ein eigenes Objekt an; der Worker selbst kann mehrfach verwendet werden.
  • Rufen Sie für jeden Frame controller.enqueue(frame) auf. Fehlende oder verworfene Frames führen zu Bildfehlern, bis der nächste Keyframe eintrifft. Mit transformer.generateKeyFrame() (Sender) bzw. transformer.sendKeyFrameRequest() (Empfänger) lässt sich ein Keyframe anfordern, etwa nach einem Schlüsselwechsel.
  • Sicherheit: Für echte E2EE verwenden Sie die Web Crypto API (z. B. AES-GCM) oder SFrame, nicht das XOR aus dem Demo-Beispiel. Bei Video sollten die Codec-Header (die ersten Bytes) oft unverschlüsselt bleiben, damit SFU-Server weiterhin funktionieren.
  • Die Zuweisung sollte möglichst vor dem Verbindungsaufbau erfolgen. Mit sender.transform = null entfernen Sie die Transformation wieder.