Signatur
Beschreibung
Der Konstruktor RTCRtpScriptTransform() gehört zur API WebRTC Encoded Transform. Er verbindet einen RTCRtpSender oder RTCRtpReceiver mit einem Worker. Die kodierten Audio- oder Videoframes laufen durch den Worker, bevor sie paketiert und versendet oder nach dem Empfang dekodiert werden.
So setzt man zum Beispiel Ende-zu-Ende-Verschlüsselung (E2EE) über einen SFU-Server um, hängt Metadaten an Frames an oder fängt Frames zum Debuggen ab. Die Verarbeitung findet komplett im Worker statt und blockiert den Haupt-Thread deshalb nicht.
Sobald das Objekt erstellt ist, löst der Worker im eigenen Kontext das Event rtctransform aus. Dessen Eigenschaft event.transformer ist ein RTCRtpScriptTransformer mit einem readable- und einem writable-Stream sowie den übergebenen options. Aktiv wird die Transformation erst, wenn du sie der Eigenschaft sender.transform bzw. receiver.transform zuweist.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $worker Pflicht | Worker | Dedizierter Worker, in dem das Event rtctransform ausgelöst wird und der die Frames verarbeitet. |
|
| $options | any | undefined | Beliebiger strukturiert klonbarer Wert, der im Worker als event.transformer.options ankommt, z. B. { operation: "encrypt" }. |
| $transfer | Array<Transferable> | [] | Übertragbare Objekte wie ein MessagePort, deren Besitz an den Worker übergeht (wie bei postMessage()). |
Rückgabewert
RTCRtpScriptTransform-Objekt. Weise es RTCRtpSender.transform oder RTCRtpReceiver.transform zu.Beispiele
Transformation für Sender und Empfänger anlegen
// main.js
const worker = new Worker('transform-worker.js');
const pc = new RTCPeerConnection();
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const [track] = stream.getVideoTracks();
const sender = pc.addTrack(track, stream);
// Ausgehende Frames verschlüsseln
sender.transform = new RTCRtpScriptTransform(worker, { operation: 'encrypt' });
// Eingehende Frames entschlüsseln
pc.addEventListener('track', ({ receiver }) => {
receiver.transform = new RTCRtpScriptTransform(worker, { operation: 'decrypt' });
});
Worker: Frames per XOR verändern (Demo-Verschlüsselung)
// transform-worker.js
const KEY = 0x5a;
const xorFrame = (frame, controller) => {
const data = new Uint8Array(frame.data);
// Bei Video die ersten Bytes (Header) unverändert lassen
const skip = frame.type === 'key' ? 10 : 3;
for (let i = skip; i < data.length; i++) data[i] ^= KEY;
frame.data = data.buffer;
controller.enqueue(frame);
};
self.addEventListener('rtctransform', ({ transformer }) => {
console.log('Operation:', transformer.options.operation);
transformer.readable
.pipeThrough(new TransformStream({ transform: xorFrame }))
.pipeTo(transformer.writable);
});
Schlüssel über einen MessagePort nachreichen
// main.js
const worker = new Worker('transform-worker.js');
const { port1, port2 } = new MessageChannel();
sender.transform = new RTCRtpScriptTransform(
worker,
{ operation: 'encrypt', port: port2 },
[port2] // Port an den Worker übertragen
);
// Später: Schlüsselwechsel
port1.postMessage({ type: 'rotate-key', keyId: 2 });
// transform-worker.js (Ausschnitt)
self.onrtctransform = ({ transformer }) => {
transformer.options.port.onmessage = ({ data }) => {
console.log('Neuer Schlüssel:', data.keyId);
};
};
// Wichtig · Fallstricke
- Browser-Unterstützung: Firefox und Safari unterstützen den Konstruktor, Chrome erst ab neueren Versionen. Ältere Chromium-Versionen kennen nur die nicht standardisierten Insertable Streams mit
RTCRtpSender.createEncodedStreams()undencodedInsertableStreams: truein derRTCPeerConnection-Konfiguration. Prüfe die Unterstützung vorab:'RTCRtpScriptTransform' in window. - Der Konstruktor steht nur im Fenster-Kontext zur Verfügung. Das Event
rtctransformwird dagegen im Worker ausgelöst. Registriere den Listener deshalb im Worker-Skript, nicht im Haupt-Thread. - Jedes Frame muss mit
controller.enqueue()weitergereicht werden, sonst stockt der Medienfluss. Übersprungene Frames führen bei Video zu Artefakten, bis der nächste Keyframe eintrifft. Mittransformer.sendKeyFrameRequest()bzw.generateKeyFrame()lässt sich ein Keyframe anfordern. - Codec-Header (z. B. VP8-Payload-Header) sollten unverschlüsselt bleiben, damit die SFU weiterhin korrekt weiterleiten kann.
- Sicherheit: Das XOR-Beispiel ist keine echte Verschlüsselung. Für E2EE solltest du
crypto.subtle(z. B. AES-GCM) mit sauberer Schlüsselverwaltung verwenden. Die Seite muss in einem sicheren Kontext (HTTPS) laufen. - Ein
RTCRtpScriptTransformkann nur einem einzigen Sender oder Empfänger zugewiesen werden. Wird es ein zweites Mal zugewiesen, wird einInvalidStateErrorausgelöst.