Signatur
Beschreibung
Das Ereignis rtctransform ist der Einstiegspunkt für WebRTC Encoded Transforms. Das sind codierte Audio- und Video-Frames, die zwischen Encoder und Paketierer (beim Senden) bzw. zwischen Depaketierer und Decoder (beim Empfangen) in einem Worker bearbeitet werden. Im Haupt-Thread erzeugen Sie ein new RTCRtpScriptTransform(worker, options) und weisen es der Eigenschaft transform eines RTCRtpSender oder RTCRtpReceiver zu. Daraufhin feuert im Worker ein rtctransform-Ereignis.
Das Ereignisobjekt ist vom Typ RTCTransformEvent. Seine Eigenschaft transformer enthält ein RTCRtpScriptTransformer mit folgenden Bestandteilen:
readable: einReadableStreammit den eingehenden codierten Frames (RTCEncodedVideoFramebzw.RTCEncodedAudioFrame),writable: einWritableStream, in den Sie die bearbeiteten Frames zurückschreiben,options: das zweite Argument desRTCRtpScriptTransform-Konstruktors, also etwa ein Name oder ein Schlüssel, mit dem sich Sender- und Empfänger-Transforms unterscheiden lassen.
Typische Anwendungen sind Ende-zu-Ende-Verschlüsselung (E2EE, etwa bei Videokonferenzen über SFUs), das Einfügen von Metadaten und die Analyse von Frames. Das Ereignis wird einmal pro gebundenem Transform ausgelöst, nicht einmal pro Frame. Die Frames selbst verarbeiten Sie danach über die Streams, meist mit readable.pipeThrough(new TransformStream(...)).pipeTo(writable).
Sie können das Ereignis mit addEventListener("rtctransform", handler) abonnieren oder die Eigenschaft onrtctransform setzen. Es bubbelt nicht und kann nicht abgebrochen werden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "rtctransform". |
|
| $listener Pflicht | (event: RTCTransformEvent) => void | Callback, der das RTCTransformEvent erhält. Über event.transformer erreichen Sie readable, writable und options. |
|
| $options | boolean | AddEventListenerOptions | false | Übliche Listener-Optionen wie once oder signal. |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler bekommt ein RTCTransformEvent.Beispiele
Haupt-Thread: Transform an Sender und Empfänger binden
// main.js
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);
// Löst im Worker ein rtctransform-Ereignis aus
sender.transform = new RTCRtpScriptTransform(worker, { role: 'sender', key: 42 });
}
pc.addEventListener('track', ({ receiver }) => {
receiver.transform = new RTCRtpScriptTransform(worker, { role: 'receiver', key: 42 });
});
Worker: Frames per addEventListener verarbeiten (einfache XOR-„Verschlüsselung“)
// e2ee-worker.js
const xorFrame = (key) => new TransformStream({
transform(frame, controller) {
const data = new Uint8Array(frame.data);
for (let i = 0; i < data.length; i++) data[i] ^= key;
frame.data = data.buffer;
controller.enqueue(frame);
}
});
self.addEventListener('rtctransform', (event) => {
const { readable, writable, options } = event.transformer;
console.log('Transform gebunden:', options.role);
readable
.pipeThrough(xorFrame(options.key))
.pipeTo(writable)
.catch((err) => console.error('Pipeline-Fehler:', err));
});
Worker: onrtctransform-Eigenschaft und Frame-Statistik
// stats-worker.js
self.onrtctransform = async ({ transformer }) => {
let count = 0;
const reader = transformer.readable.getReader();
const writer = transformer.writable.getWriter();
while (true) {
const { value: frame, done } = await reader.read();
if (done) break;
if (++count % 100 === 0) {
console.log(`${count} Frames, Typ: ${frame.type ?? 'audio'}, Bytes: ${frame.data.byteLength}`);
}
await writer.write(frame); // unverändert weiterreichen
}
};
// Handler wieder entfernen:
// self.onrtctransform = null;
Feature-Erkennung mit Fallback
// main.js
const supportsScriptTransform = 'RTCRtpScriptTransform' in window;
const supportsLegacyStreams = 'createEncodedStreams' in RTCRtpSender.prototype;
console.log({ supportsScriptTransform, supportsLegacyStreams });
// Ohne RTCRtpScriptTransform: Streams über sender.createEncodedStreams()
// holen und per postMessage (transferierbar) an den Worker schicken.
// Wichtig · Fallstricke
- Browser-Unterstützung: Safari ab 15.4 und Firefox ab 117 unterstützen
RTCRtpScriptTransform. Chromium-Browser setzen dagegen lange auf die nicht standardisierte APIcreateEncodedStreams()(setztencodedInsertableStreams: truein derRTCPeerConnection-Konfiguration voraus). Prüfen Sie beide Wege per Feature-Erkennung. - Das Ereignis wird nur in Dedicated Workern ausgelöst, nicht in Shared Workern, Service Workern oder im Haupt-Thread.
- Das Ereignis feuert einmal pro Transform. Wer im Handler auf einzelne Frames wartet, hat das Modell falsch verstanden: Die Frames kommen über
transformer.readable. - Wenn ein Worker mehrere Transforms bedient (Audio/Video, Senden/Empfangen), unterscheiden Sie diese über
transformer.options. Frames, die Sie nicht zurück inwritableschreiben, werden verworfen. Das zeigt sich als eingefrorenes Video oder als Tonaussetzer. - Die Beispiel-XOR-Verschlüsselung ist keine echte Sicherheit. Für E2EE nutzen Sie
crypto.subtle(z. B. AES-GCM). Lassen Sie bei Video die Codec-Header der Keyframes unverschlüsselt, damit die SFU weiterhin richtig weiterleiten kann. Alternativ kommt SFrame infrage. - Bleibt die Verarbeitung pro Frame zu lange hängen, steigt die Latenz. Blockierende Arbeit im Worker sollten Sie daher vermeiden.