Signatur
Beschreibung
Die Eigenschaft transform eines RTCRtpReceiver gehört zur WebRTC Encoded Transform-API. Weist du ihr ein RTCRtpScriptTransform zu, fügt der Browser einen Verarbeitungsschritt in die Empfangspipeline ein: Jedes eingehende, noch codierte Frame (RTCEncodedVideoFrame bzw. RTCEncodedAudioFrame) wird an einen Worker übergeben. Dort kannst du es über einen TransformStream verändern und dann an den Decoder weiterreichen.
Typische Anwendungen sind Ende-zu-Ende-Verschlüsselung (z. B. SFrame oder eigene AES-GCM-Schemata bei Konferenzen über SFU-Server), das Auslesen oder Entfernen eingebetteter Metadaten und das Messen von Frame-Größen oder Zeitstempeln. Auf der Sendeseite gibt es dazu das Gegenstück RTCRtpSender.transform.
Im Worker löst das Zuweisen das Ereignis rtctransform aus. Dessen Eigenschaft event.transformer (RTCRtpScriptTransformer) stellt einen readable- und einen writable-Stream bereit, außerdem die beim Erzeugen übergebenen options. Setzt du transform auf null, wird die Transformation wieder aus der Pipeline entfernt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $value | RTCRtpScriptTransform | null | null | Beim Setzen: ein mit new RTCRtpScriptTransform(worker, options, transfer) erzeugtes Objekt oder null, um die Transformation zu entfernen. |
Rückgabewert
RTCRtpScriptTransform oder null, wenn keine Transformation aktiv ist.Beispiele
Transformation beim Empfang eines Tracks setzen (Haupt-Thread)
const worker = new Worker('receiver-worker.js');
const pc = new RTCPeerConnection();
pc.addEventListener('track', (event) => {
// Optionen landen im Worker unter transformer.options
event.receiver.transform = new RTCRtpScriptTransform(worker, {
operation: 'decrypt',
kind: event.track.kind
});
document.querySelector('video').srcObject = event.streams[0];
});
Frames im Worker entschlüsseln (receiver-worker.js)
// receiver-worker.js
const KEY_BYTE = 0x5a; // Demo-XOR – in der Praxis AES-GCM über crypto.subtle
const decrypt = (encodedFrame, controller) => {
const view = new Uint8Array(encodedFrame.data);
const out = new Uint8Array(view.length);
for (let i = 0; i < view.length; i++) out[i] = view[i] ^ KEY_BYTE;
encodedFrame.data = out.buffer;
controller.enqueue(encodedFrame);
};
self.addEventListener('rtctransform', (event) => {
const { readable, writable, options } = event.transformer;
console.log('Transform aktiv:', options.operation, options.kind);
readable
.pipeThrough(new TransformStream({ transform: decrypt }))
.pipeTo(writable);
});
Funktionsprüfung und Entfernen der Transformation
const supportsEncodedTransform = 'RTCRtpScriptTransform' in window &&
'transform' in RTCRtpReceiver.prototype;
const toggleDecryption = (receiver, worker, enabled) => {
if (!supportsEncodedTransform) {
console.warn('WebRTC Encoded Transform wird nicht unterstützt');
return;
}
receiver.transform = enabled
? new RTCRtpScriptTransform(worker, { operation: 'decrypt' })
: null;
console.log('Aktiv:', receiver.transform !== null);
};
// Wichtig · Fallstricke
- Browser-Unterstützung: Standardkonform in Firefox (ab 117) und Safari (ab 15.4). Chromium-Browser haben lange nur das nicht standardisierte
receiver.createEncodedStreams()angeboten, das die OptionencodedInsertableStreams: trueimRTCPeerConnection-Konstruktor voraussetzt. Prüfe die Unterstützung deshalb vorher und sieh bei Bedarf einen Fallback vor. - Die Transformation läuft immer in einem Worker. Das Ereignis
rtctransformwird dort ausgelöst, nicht im Haupt-Thread. - Rufe
controller.enqueue()für jedes Frame auf. Frames, die du verwirfst oder die sich nicht entschlüsseln lassen, fehlen danach im Bild (Artefakte, eingefrorenes Video) oder im Ton. Kommt ein Keyframe nicht an, fordere auf der Sendeseite mitgenerateKeyFrame()ein neues an. - Bei Video bleiben je nach Codec einige Header-Bytes unverschlüsselt, zum Beispiel bei VP8 die ersten 1–10 Bytes. Werden sie verändert, können Zwischenstationen (SFU) und Paketierung scheitern.
- Ein
RTCRtpScriptTransformkann nur einem einzigen Sender oder Empfänger zugewiesen werden. Jeder weitere Versuch löst einen Fehler aus. - Sicherheit: Die Beispiel-XOR-Verschlüsselung schützt nicht. Für echte Ende-zu-Ende-Verschlüsselung nimm
crypto.subtle(AES-GCM) und einen sicheren Schlüsselaustausch.